如何使用bash提升版本号

15
我想知道如何使用bash将版本号中的最后一位数字增加。例如:
VERSION=1.9.0.9
NEXT_VERSION=1.9.0.10

编辑:版本号仅包含自然数。

解决方案是否可以通用化以处理版本号中的任意部分。

例如:

1.2
1.2.3
1.2.3.4
1.2.3.4.5
4个回答

27

简而言之:

VERSION=1.9.0.9
echo $VERSION | awk -F. '/[0-9]+\./{$NF++;print}' OFS=.
# will print 1.9.0.10

如需详细解释,请继续阅读。


让我们从基础开始,froogz3301提供了answer

VERSIONS="
1.2.3.4.4
1.2.3.4.5.6.7.7
1.9.9
1.9.0.9
"

for VERSION in $VERSIONS; do 
    echo $VERSION | awk -F. '{$NF = $NF + 1;} 1' | sed 's/ /./g'
done

我们如何改进这个程序呢?以下是从众多评论中提取出来的一堆想法。

程序中的尾随 '1' 对其操作至关重要,但这并不是最明确的做法。结尾处的奇怪的 '1' 是一个布尔值,因此匹配每一行并触发默认动作(因为在它后面的花括号内没有动作),即将 $0(读取的行)打印出来,并由前一个命令进行修改。

因此,为什么不使用这个 awk 命令,以取代 sed 命令呢?

awk -F. '{$NF+=1; OFS="."; print $0}'

当然,我们可以分几个阶段进一步完善事情。您可以使用bash中的'<<<'字符串重定向运算符来避免使用管道:

awk -F. '...' <<< $VERSION

下一个观察结果是,给定一系列行,awk 可以处理它们的所有内容,只需执行一次。
echo "$VERSIONS" | awk -F. '/[0-9]+\./{$NF+=1;OFS=".";print}'

没有 for 循环。双引号包裹的 "$VERSION" 保留了字符串中的换行符。管道仍然是不必要的,导致:

awk -F. '/[0-9]+\./{$NF+=1;OFS=".";print}' <<< "$VERSIONS"

正则表达式会忽略 $VERSION 中的空行,只处理包含数字和点号的行。当然,在每行中设置 OFS 有些笨拙,'+=1' 可以缩写为 '++',所以你可以使用:
awk -F. '/[0-9]+\./{$NF++;print}' OFS=. <<< "$VERSIONS"

(或者您可以在程序中包含 'BEGIN{OFS="."}',但这样做会显得冗长。

'<<<' 符号仅受 Bash 支持,而不受 Korn、Bourne 或其他 POSIX shell 的支持(除非作为与 Bash 符号相似的非标准扩展)。无论您能够获取到哪个版本的 awk,都将支持 AWK 程序(但命令行上的变量赋值不受旧的 UNIX 第七版 AWK 的支持)。


1
谢谢,我正在使用以下代码在Makefile中升级图表版本号:## bump-%: bump the % chart version number bump-%: docs lint update-deps @echo "$$(awk -F. '{if ($$0 ~ /version: [0-9]+\./) {$$NF++;print} else {print}}' OFS=. $(CHARTS_FOLDER)/$*/Chart.yaml)" > $(CHARTS_FOLDER)/$*/Chart.yaml - Luis Davim

10

我想出了这个。

VERSIONS="
1.2.3.4.4
1.2.3.4.5.6.7.7
1.9.9
1.9.0.9
"

for VERSION in $VERSIONS; do 
    echo $VERSION | awk -F. '{$NF = $NF + 1;} 1' | sed 's/ /./g'
done

1
感谢您提供更加优雅的解决方案。 - froogz3301
当然,在每一行中设置OFS有点笨拙,而'+=1'可以缩写为'++',所以你可以使用:awk -F. '/[0-9]+\./{$NF++;print}' OFS=. <<< "$VERSIONS"(或者你可以在程序中包含'BEGIN{OFS="."}',但那太啰嗦了)。 - Jonathan Leffler
2
@Jonathan:结尾处的1是一个布尔值,用于强制执行默认操作——即print。它与前置的1不同,前者仅选中输入的第一行。对于递增,$NF++也可以使用。您只需要设置一次输出定界符。结果:awk -F. '{$NF++}1' OFS=.awk -F. '1{$NF++}1' OFS=.。虽然我更喜欢显式打印而不是隐式打印,但是$0并不必要:awk -F. '{$NF++; print}' OFS=.OFS也可以通过-v参数或在BEGIN语句中进行设置。我看到你说了我正在打字的几件事情。 - Dennis Williamson
@Jonathan:任何正确的东西都可以工作。也许更好的缩写应该是 awk -F. '{$NF++}"print"' OFS=. ... 哦,等等... ;) - Dennis Williamson
@Dennis:也许我们应该建立一个社区维基扩展答案,加上这个评论? - Jonathan Leffler
显示剩余2条评论

3
if [[ "$VERSION" == *.* ]]; then
    majorpart="${VERSION%.*}."
else
    majorpart=""
fi
minorpart="${VERSION##*.}"
NEXT_VERSION="$majorpart$((minorpart+1))"

警告:如果版本号的次要部分不符合预期格式(整数,没有前导零),可能会出现问题。一些例子:"1.033" -> "1.28"(因为 033 是 27 的八进制表示),"1.2.b" -> "1.2.1"(除非 b 是一个已定义的变量,否则它将被视为 0),"1.2.3a" -> 错误("3a" 不是一个数字)。根据您想要涵盖的情况数量,这可以变得任意复杂。


2
你可以通过以下方式强制将数字(例如“033”)解释为十进制:NEXT_VERSION="$majorpart$((10#$minorpart+1))",这样“033”将变成“34”。这样做是个好主意,因为“038”会产生错误。但是,它会导致你的“b”示例产生错误。 - Dennis Williamson

2

好的,Jonathan Leffler已经 回答了这个问题,但我将解决方案推广到接受任意差异(通过awk参数versionDiff传递):

VERSION="1.4.1.2"
awk -v versionDiff="0.1" -F. -f bump.awk OFS=. <<< "$VERSION"

结果将是:

1.5.0.0

当最后一个非零 versionDiff 数字之后的数字被归零时。

以及 bump.awk

/[0-9]+\./ {
  n = split(versionDiff, versions, ".")
  if(n>NF) nIter=n; else nIter=NF
  lastNonzero = nIter
  for(i = 1; i <= nIter; ++i) {
    if(int(versions[i]) > 0) {
      lastNonzero = i
    }
    $i = versions[i] + $i
  }
  for(i = lastNonzero+1; i <= nIter; ++i) {
    $i = 0
  }
  print
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接