如何获取Maven项目版本到bash命令行

之前我在如何从命令行更改Maven项目版本上提出了一个问题,这导致我到了一个新的问题。

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

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

那么,是否有任何简单的方法将${project.version}的值获取到命令行?

解决方案

我必须手动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)
185670 次浏览

Maven帮助插件以某种方式已经为此提出了一些东西:

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

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

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

在评论中注意到 by 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输出中提取project.version

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

我注意到输出中出现了一些虚假的Downloaded:行,它们破坏了我原来的赋值。这是我选定的滤镜;希望能有所帮助!

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

编辑

不是100%确定为什么,但是当在Jenkins中运行后构建脚本时,输出是[INFO]version,例如[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')

编辑

这里还有最后一点。我发现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]Download消息的无关输出:

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-SNAPSHOT

因此,在一个简单的bash脚本中使用project.version:

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插件的解决方案要好得多,但仍然比它需要的更复杂。对我来说很简单:

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-plugin的输出。

例如,要将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服务器如詹金斯可以使用它):

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

生成的version.properties文件将如下所示:

PROJECT_VERSION=1.0.0-SNAPSHOT

最重要的是,候选发布版还允许你基于你在POM中定义的API版本来设置版本你的项目(这是你可能在CI服务器上做的事情)。

如果你想看一个Release Candidate被用作Maven生命周期一部分的例子,可以看看我的另一个开源项目——为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插件注入到作业环境中:

enter image description here

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

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

在我看来,上面的答案是非常垃圾的,你必须使用一堆grep来破解maven控制台输出。为什么不使用正确的工具呢?使用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=WARN
  • org.slf4j.simpleLogger.log.org.apache.maven.plugins.help=INFO

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}'

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

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

http://www.faqs.org/docs/artu/ch01s06.html(不再可用) “Unix之路”; (ii)期望每个程序的输出成为另一个未知程序的输入。不要用无关的信息使输出变得混乱。避免使用严格的柱状或二进制输入格式。不要坚持交互式输入。 它到底是什么意思? 你的输出应该是:

  • greitable ":(一个记录;每一行)
  • “裁剪”:(分隔的“字段”;)
  • 退出码:0表示成功,非0表示失败。
  • 消息传递(stderr) vs.输出(stdout)

(参考:用ruby创建很棒的命令行应用程序by Dave Copeland 2012.1.18 https://youtu.be/1ILEw6Qca3U?t=372)

  1. 诚实我认为戴夫·科普兰说得对,他说专家与他人不公平。所以我决定看看maven的源代码以及maven-help-plugin的源代码。似乎他们已经修复了maven的-q开关(当时我使用的是3.5.3版本),所以现在如果您通过了它,您将不会得到所有阻止maven在自动化脚本中使用的恼人的无意义日志记录。所以你应该能够使用这样的东西:

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

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

  1. 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 package之后,maven-archiver插件创建了包含如下内容的target/maven-archiver/pom.properties

version=0.0.1-SNAPSHOT
groupId=somegroup
artifactId=someArtifact

我使用bash来执行它

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

然后

echo $version
0.0.1-SNAPSHOT

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

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

cat pom.xml | grep "" | head -n 1 | sed -e "s/version//g" | Sed -e "s/\s*[<>/]*//g"

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

这是上面的编辑

cat pom.xml | grep "" | head -n 1 | sed -e "s/version//g" | Sed -e "s/\s*[<>/]*//g"

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

grep”“pom.xml |头- n 1 | sed - e " s /版本/ / g " | sed - e " s / \ s * [& lt; > /] * / / 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

优点:

  • 这在所有操作系统和所有外壳上工作良好。
  • 不需要任何外部工具!
  • [重要的]即使项目版本继承自父版本pom.xml,此方法也有效

注意:

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

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

<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

如果你想获取groupIdartifactId,检查这个答案

我在我的特拉维斯工作期间需要这个要求,但有多个值。 我从这个解决方案开始,但当调用多次时,这是非常缓慢的(我需要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 install 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"

获得mavens版本最简单的方法是这样的:

mvn -v | head -1 | cut -d ' ' -f 3

打印:3.8.5