之前我发布了一个关于如何从命令行更改Maven项目版本的问题,这导致我遇到了一个新问题。

以前我能够获得版本号,因为版本存储为一个属性,很容易从命令行(bash)进行grep和解析。现在pom.xml <version>元素用于此,它不再是唯一的,因为所有依赖项(也许其他一些依赖项也使用它)。我认为,如果没有用于解析XML的外部工具或一些非常能感知上下文的sed命令,就无法用bash脚本获得当前版本号。

在我看来,最干净的解决方案是Maven发布这个版本信息。我想写一个自定义的maven插件检索不同的属性,但我想我应该先问这里。

那么,是否有任何简单的方法来获取${project的值。版本}到命令行?

解决方案

我必须手动cd到该目录,但这可以很容易地做到。在我的bash脚本我有:

version=`cd $project_loc && mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | sed -n -e '/^\[.*\]/ !{ /^[0-9]/ { p; q } }'`

这就得到了我可以改进的当前版本。Grepping可能更简单,但我认为我想尽可能健壮,所以我对以数字开头的第一行感到满意,并尝试将其作为版本号处理。

# Advances the last number of the given version string by one.
function advance_version () {
    local v=$1
    # Get the last number. First remove any suffixes (such as '-SNAPSHOT').
    local cleaned=`echo $v | sed -e 's/[^0-9][^0-9]*$//'`
    local last_num=`echo $cleaned | sed -e 's/[0-9]*\.//g'`
    local next_num=$(($last_num+1))
    # Finally replace the last number in version string with the new one.
    echo $v | sed -e "s/[0-9][0-9]*\([^0-9]*\)$/$next_num/"
}

我通过调用:

new_version=$(advance_version $version)

Maven帮助插件已经为此提出了一些建议:

help:evaluate以交互模式计算用户给出的Maven表达式。

下面是你如何在命令行上调用它来获得${project.version}:

mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate \
    -Dexpression=project.version

正如Seb T的注释中所指出的,要只打印没有maven INFO日志的版本,另外使用-q -DforceStdout:

mvn help:evaluate -Dexpression=project.version -q -DforceStdout

mvn help:evaluate -Dexpression=project.version | sed -e 1h -e '2,3{H;g}' -e '/\[INFO\] BUILD SUCCESS/ q' -e '1,2d' -e '{N;D}' | sed -e '1q'

我只是添加了一个小的sed过滤器改进,我最近实现了提取项目。来自maven输出的版本。


mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | grep -v '\['

我注意到一些虚假的下载:行来的输出打破了我原来的任务。这是我选定的滤镜;希望能有所帮助!

version=$(mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | egrep -v '^\[|Downloading:' | tr -d ' \n')

EDIT

不是100%确定为什么,但是当在Jenkins中运行后构建脚本时,输出是[INFO]版本,例如[INFO]0.3.2。

我将输出转储到一个文件,并通过我的第一个过滤器直接从BASH运行,它工作得很好。所以,再次,不确定在詹金斯的土地上发生了什么。

为了在Jenkins中获得100%,我添加了后续sed过滤器;这是我的最新消息

version=$(mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | egrep -v '^\[|Downloading:' | tr -d ' \n' | sed -E 's/\[.*\]//g')

EDIT

这里还有最后一点。我发现tr仍然会导致/r/n0.3.2这样的东西(再次只有在通过Jenkins运行时)。切换到awk,问题就消失了!我最终的工作成果

mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version \
| egrep -v '^\[|Downloading:' | sed 's/[^0-9\.]//g' | awk 1 ORS=''

如果您不介意将版本写入临时文件,那么还有另一种解决方案(不含grep/sed)适合我。(编辑:请参阅rjrjr的回答,这是一个更简单的解决方案,没有任何临时文件的麻烦)

我使用Exec Maven插件和echo二进制文件。与Maven Help Plugin相反,Exec Plugin允许输出重定向到文件,可以用来绕过grep/sed,甚至可以解析奇怪的东西,比如多行版本字符串(版本标记中带有CDATA块),至少在一定程度上是这样。

#!/usr/bin/env sh

MVN_VERSION=""
VERSION_FILE=$( mktemp mvn_project_version_XXXXX )
trap "rm -f -- \"$VERSION_FILE\"" INT EXIT

mvn -Dexec.executable="echo" \
    -Dexec.args='${project.version}' \
    -Dexec.outputFile="$VERSION_FILE" \
    --non-recursive \
    --batch-mode \
    org.codehaus.mojo:exec-maven-plugin:1.3.1:exec > /dev/null 2>&1 ||
    { echo "Maven invocation failed!" 1>&2; exit 1; }

# if you just care about the first line of the version, which will be
# sufficent for pretty much every use case I can imagine, you can use
# the read builtin
[ -s "$VERSION_FILE" ] && read -r MVN_VERSION < "$VERSION_FILE"

# Otherwise, you could use cat.
# Note that this still has issues when there are leading whitespaces
# in the multiline version string
#MVN_VERSION=$( cat "$VERSION_FILE" )

printf "Maven project version: %s\n" "$MVN_VERSION"

易于理解的一体化解决方案,输出maven项目版本,并抑制来自[INFO]和下载消息的无关输出:

mvn -o org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | grep -v '\['

同样的东西,但是分成了两条线:

mvn -o org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate \
      -Dexpression=project.version | grep -v '\['

输出:4.3快照

所以,使用你的项目。版本在一个简单的bash脚本:

projectVersion=`mvn -o org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | grep -v '\['`
cd "target/"$projectVersion"-build"

本页上的其他解决方案似乎并没有把所有的技巧结合在一起。


Tom使用Exec Maven Plugin的解决方案要好得多,但仍然比实际需要的更复杂。对我来说很简单:

MVN_VERSION=$(mvn -q \
    -Dexec.executable=echo \
    -Dexec.args='${project.version}' \
    --non-recursive \
    exec:exec)

应该更容易,因为这个错误在maven-help-plugin 3.0.0中得到了修复:MPH-99 Evaluate在安静模式下没有输出。


python -c "import xml.etree.ElementTree as ET; \
  print(ET.parse(open('pom.xml')).getroot().find( \
  '{http://maven.apache.org/POM/4.0.0}version').text)"

只要你有python 2.5或更高版本,这应该可以工作。如果您的版本比这个低,请安装python-lxml并将导入更改为lxml.etree。这种方法很快,而且不需要下载任何额外的插件。它还适用于不使用xmllint进行验证的格式错误的pom.xml文件,比如我需要解析的那些文件。在Mac和Linux上测试。


VERSION=$(head -50 pom.xml | awk -F'>' '/SNAPSHOT/ {print $2}' | awk -F'<' '{print $1}')

这是我用来获得版本号的,认为会有更好的maven方法来这样做


我最近开发了发布候选Maven插件来解决这个确切的问题,这样您就不必求助于任何hack shell脚本并解析Maven -help-插件的输出。

例如,要将Maven项目的版本打印到终端,运行:

mvn com.smartcodeltd:release-candidate-maven-plugin:LATEST:version

输出类似于maven-help-plugin:

[INFO] Detected version: '1.0.0-SNAPSHOT'
1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

但是,您也可以指定任意的输出格式(这样CI服务器(如TeamCity)就可以从日志中获取版本):

mvn com.smartcodeltd:release-candidate-maven-plugin:LATEST:version \
   -DoutputTemplate="##teamcity[setParameter name='env.PROJECT_VERSION' value='{{ version }}']"

结果是:

[INFO] Detected version: '1.0.0-SNAPSHOT'
##teamcity[setParameter name='env.PROJECT_VERSION' value='1.0.0-SNAPSHOT']
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

将输出保存到一个文件中(这样CI服务器(如Jenkins)就可以使用它):

mvn com.smartcodeltd:release-candidate-maven-plugin:LATEST:version \
   -DoutputTemplate="PROJECT_VERSION={{ version }}" \
   -DoutputUri="file://\${project.basedir}/version.properties"

最终的版本。属性文件将如下所示:

PROJECT_VERSION=1.0.0-SNAPSHOT

在上述所有功能之上,Release Candidate还允许您基于在POM中定义的API版本设置项目的版本(这可能是您在CI服务器上所做的事情)。

如果您希望看到一个Release Candidate作为Maven生命周期一部分使用的示例,请查看我的另一个开源项目——Build Monitor for Jenkins的pom.xml。


这适用于我,离线和不依赖于mvn:

VERSION=$(grep --max-count=1 '<version>' <your_path>/pom.xml | awk -F '>' '{ print $2 }' | awk -F '<' '{ print $1 }')
echo $VERSION

Exec插件不需要任何输出解析,因为输出可以重定向到文件中,并通过EnvInject插件注入到作业环境中:


这将避免从输出中删除日志项的需要:

mvn -Dexec.executable='echo' -Dexec.args='${project.version}' --non-recursive exec:exec -q

为什么不使用正确的工具呢?使用xpath语法是检索版本号的最佳方法,因为它是访问XML数据结构的预期方法。下面的表达式使用元素的“本地名称”遍历pom,换句话说,忽略xml中可能存在也可能不存在的名称空间声明。

xmllint --xpath "//*[local-name()='project']/*[local-name()='version']/text()" pom.xml

我在用其他答案的时候遇到了一些特例,所以这是另一种选择。

version=$(printf 'VER\t${project.version}' | mvn help:evaluate | grep '^VER' | cut -f2)

只是为了记录,可以直接在命令行中配置Maven的Simple SLF4J日志记录,只输出我们需要的配置:

org.slf4j.simpleLogger.defaultLogLevel =警告和 org.slf4j.simpleLogger.log.org.apache.maven.plugins.help =信息

详见http://www.slf4j.org/api/org/slf4j/impl/SimpleLogger.html

MAVEN_OPTS="\
-Dorg.slf4j.simpleLogger.defaultLogLevel=WARN \
-Dorg.slf4j.simpleLogger.log.org.apache.maven.plugins.help=INFO" \
mvn help:evaluate -o -Dexpression=project.version

因此,可以简单地运行tail -1并得到:

$ MAVEN_OPTS="\
-Dorg.slf4j.simpleLogger.defaultLogLevel=WARN \
-Dorg.slf4j.simpleLogger.log.org.apache.maven.plugins.help=INFO" \
mvn help:evaluate -o -Dexpression=project.version | tail -1

1.0.0-SNAPSHOT

注意,这是一行程序。MAVEN_OPTS仅为这个特定的mvn执行而被重写。


要么让mvn给出答案(正如大多数答案所建议的那样),要么从pom.xml中提取答案。第二种方法的唯一缺点是可以很容易地提取<version/>标记的值,但只有当它是文字时,即不是Maven属性时,它才有意义。我选择了这种方法,因为:

mvn太啰嗦了,我只是不喜欢过滤它的输出。 与读取pom.xml相比,启动mvn非常慢。 我总是在<version/>中使用文字值。

Mvn-version是一个ZSH shell脚本,它使用xmlstarlet读取pom.xml并打印项目的版本(如果存在)或父项目的版本(如果存在):

$ mvn-version .
1.0.0-SNAPSHOT

优点是它比运行mvn快得多:

$ time mvn-version .
1.1.0-SNAPSHOT
mvn-version .  0.01s user 0.01s system 75% cpu 0.019 total

$ time mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate \
> -Dexpression=project.version
mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate   4.17s user 0.21s system 240% cpu 1.823 total

我的机器上的差异超过两个数量级。


一个简单的专家解决方案

mvn -q -N org.codehaus.mojo:exec-maven-plugin:1.3.1:exec \
    -Dexec.executable='echo' \
    -Dexec.args='${project.version}'

并为奖金分解析的版本的一部分

mvn -q -N org.codehaus.mojo:build-helper-maven-plugin:3.0.0:parse-version \
    org.codehaus.mojo:exec-maven-plugin:1.3.1:exec \
    -Dexec.executable='echo' \
    -Dexec.args='${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}'

在做了一些研究之后,我发现了以下几点:

Maven一直被指责,因为与DevOps工具的集成并不容易,因为它没有遵循一些关于CLI工具的良好实践,例如:

http://www.faqs.org/docs/artu/ch01s06.html (not available anymore) "The Unix Way" (ii) Expect the output of every program to become the input of another, as yet unknown, program. Don't clutter output with extraneous information. Avoid stringently columnar or binary input formats. Don't insist on interactive input. What does it actually mean? Your output should be: "grepable": (One "record" per line) "cutable": (Delimited "fields") exit codes: 0 for success, nonzero for failure. messaging (stderr) vs. output (stdout)

(参考:Dave Copeland于2012年1月18日用ruby制作很棒的命令行应用程序https://youtu.be/1ILEw6Qca3U?t=372)

Honesty I think Dave Copeland was right when he said that maven does't play fairly with others. So I decided to give a look to maven's source code as well as to maven-help-plugin's source code as well. It seems that they have fixed a little bit the maven's -q switch (I was using version 3.5.3 at that time), so now if you pass it, you won't get all the annoying non-sense logging stuff that prevents maven from being used within automated scripts. So you should be able to use something like this: mvn help:evaluate -Dexpression=project.version -q

问题是这个命令不会输出任何东西,因为默认情况下,帮助插件通过记录器输出,而记录器已被-q开关静音。(当时插件的最新可用版本是2018年6月3日发布的3.1.0)

Karl Heinz Marbaise (https://github.com/khmarbaise)通过添加一个可选参数来修复它,允许你以以下方式调用它: mvn help:evaluate -Dexpression=project。version -q -DforceStdout

提交描述可在以下网站获得:(https://github.com/apache/maven-help-plugin/commit/316656983d780c04031bbadd97d4ab245c84d014)

同样,您应该始终验证命令的退出码,并在unix上将所有stderr重定向到/dev/null。


我找到了适合自己的平衡。在mvn包后,maven-archiver插件创建了target/maven-archiver/pom。属性具有如下内容

version=0.0.1-SNAPSHOT
groupId=somegroup
artifactId=someArtifact

我使用bash来执行它

. ./target/maven-archiver/pom.properties

then

echo $version
0.0.1-SNAPSHOT

当然,执行这个文件一点也不安全,但是可以很容易地将执行转换为perl或bash脚本,以便从该文件读取和设置环境变量。


我正在我的unix shell中使用一行程序…

油漆pom . xml | grep " |头-n 1 | sed e, s -版/ g”| sed -e "s/\s*[<>/]*/ g"

您可以将这一行隐藏在shell脚本中或作为别名。


这是上面的编辑

油漆pom . xml | grep " |头-n 1 | sed e, s -版/ g”| sed -e "s/\s*[<>/]*/ g"

我在cmdline上测试了一下,效果很好

grep " pom . xml |头-n 1 | sed e, s -版/ g”| sed e,“s / \ s * [< > /] * / / g

是同一事物的另一个版本。我需要在没有安装mvn的情况下在Jenkins CI中获得k8s的版本号,所以这是最有帮助的

谢谢所有。


还有一个不需要Maven的选项:

grep -oPm1 "(?<=<version>)[^<]+" "pom.xml"

这是最干净的解决方案:

mvn org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate \
-Dexpression=project.version -q -DforceStdout

优点:

这在所有操作系统和所有shell上都可以正常工作。 不需要任何外部工具! [重要]即使项目版本从父pom.xml继承,这也有效

注意:

maven-help-plugin 3.2.0(及以上版本)有强制输出选项。您可以从artifactory的mvn-help-plugin可用版本列表中替换上述命令中的3.2.0,如果可用的话。 选项-q抑制详细消息([INFO], [WARN]等)

或者,你可以在你的pom.xml中添加这个条目,在插件部分下:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-help-plugin</artifactId>
    <version>3.2.0</version>
</plugin>

然后按如下简洁地运行上述命令:

mvn help:evaluate -Dexpression=project.groupId -q -DforceStdout

如果您还想获取groupId和artifactId,请检查这个答案。


在我的特拉维斯工作期间,我需要的正是这个要求,但有多个值。 我从这个解决方案开始,但当调用多次这是非常缓慢的(我需要5个表达式)。

我写了一个简单的maven插件来将pom.xml的值提取到.sh文件中。

https://github.com/famaridon/ci-tools-maven-plugin

mvn com.famaridon:ci-tools-maven-plugin:0.0.1-SNAPSHOT:environment -Dexpressions=project.artifactId,project.version,project.groupId,project.build.sourceEncoding

会产生:

#!/usr/bin/env bash
CI_TOOLS_PROJECT_ARTIFACTID='ci-tools-maven-plugin';
CI_TOOLS_PROJECT_VERSION='0.0.1-SNAPSHOT';
CI_TOOLS_PROJECT_GROUPID='com.famaridon';
CI_TOOLS_PROJECT_BUILD_SOURCEENCODING='UTF-8';

现在您可以简单地获取该文件的源代码

source .target/ci-tools-env.sh

玩得开心。


这是迄今为止最简单的bash剪切和粘贴解决方案:

VERSION=$(mvn exec:exec -Dexec.executable='echo' -Dexec.args='${project.version}' --non-recursive -q)
echo $VERSION

它的回声

1.4

基于这个问题,我使用下面的脚本自动增加所有maven父/子模块中的版本号:

#!/usr/bin/env bash  

# Advances the last number of the given version string by one.  
function advance\_version () {  
    local v=$1  
    \# Get the last number. First remove any suffixes (such as '-SNAPSHOT').  
  local cleaned=\`echo $v | sed \-e 's/\[^0-9\]\[^0-9\]\*$//'\`  
 local last\_num=\`echo $cleaned | sed \-e 's/\[0-9\]\*\\.//g'\`  
 local next\_num=$(($last\_num+1))  
  \# Finally replace the last number in version string with the new one.  
  echo $v | sed \-e "s/\[0-9\]\[0-9\]\*\\(\[^0-9\]\*\\)$/$next\_num/"  
}  

version=$(mvn org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=project.version -q -DforceStdout)  

new\_version=$(advance\_version $version)  

mvn versions:set -DnewVersion=${new\_version} -DprocessAllModules -DgenerateBackupPoms=false

一种替代方法是使用yq (https://github.com/kislyuk/yq)进行解析,如下所示:

cat pom.xml | xq -r '.project.version'

注意,可执行文件是xq而不是yq

为了获得xq,像pip安装yq那样安装yq


在只有busybox可用的docker容器中,我使用awk。

version=$(
 awk '
  /<dependenc/{exit}
  /<parent>/{parent++};
  /<version>/{
    if (parent == 1) {
      sub(/.*<version>/, "");
      sub(/<.*/, "");
      parent_version = $0;
    } else {
      sub(/.*<version>/, "");
      sub(/<.*/, "");
      version = $0;
      exit
    }
  }
  /<\/parent>/{parent--};
  END {
    print (version == "") ? parent_version : version
  }' pom.xml
)

注:

如果没有给定工件版本,则打印父版本。 工件和父版本之后需要依赖关系。(这很容易克服,但我不需要它。)


将以下插件添加到pom.xml中

    ...
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-help-plugin</artifactId>
        <version>3.2.0</version>
        <executions>
            <execution>
                <id>generate-version-file</id>
                <phase>prepare-package</phase>
                <goals>
                    <goal>evaluate</goal>
                </goals>
                <configuration>
                    <expression>project.version</expression>
                    <output>${project.build.directory}/version.txt</output>
                </configuration>
            </execution>
        </executions>
    </plugin>
    ...

这将生成一个target/version.txt文件,作为构建的正常部分。

然后你需要做的就是把它放到shell中:

#!/bin/sh
value=`cat target/version.txt`
echo "$value"
#!/bin/bash
value=$(<target/version.txt)
echo "$value"

VERSION=$(mvn -version | grep 'Apache Maven' | grep -o '[0-9].[0-9].[0-9]')
echo $VERSION

最快捷的方式,让你只看到文字,而没有任何装饰

mvn help:evaluate -q -DforceStdout -D"expression=project.version"