在shell脚本中比较两个版本号

3

我有一个名为file1的文件,其内容如下,包含当前版本和期望版本号:

CurrV:1.5.2
ExpecV:1.8.1

我想编写一个bash脚本来比较这两个值,如果ExpecV>=CurrV,那么我应该echo SUCCESS,否则我应该echo FAILURE
到目前为止,我已经写了这个东西,但不确定如何继续:
#!/bin/bash
 ## Code already written to fetch `ExpecV` and `CurrV` from `file1`
 echo $ExpecV | grep $CurrV > /dev/null
 if [ $? -eq 0 ]
    then
        echo SUCCESS
    else
        echo FAILURE
 fi

根据您在我的答案上留下的评论,我认为您想要的条件是:“如果ExpecV > CurrV,则打印FAILURE,否则打印SUCCESS”。 - Benjamin W.
其他答案可以在这里找到:https://dev59.com/32865IYBdhLWcg3wCp4_。本质上,它是关于在bash中比较两个版本字符串的问题。 - ikaerom
4个回答

5
您可以尝试。
if [ $(echo "${CurrV}\n${ExpecV}"|sort|head -1) != "${CurrV}" ]; then ...

5
在那里使用 |sort -V| 取代 |sort|,以便按自然版本号语义进行真正的排序。 在我的情况下,echo 需要是 echo -e,这样它才会将 \n 扩展为换行符。 - Jürgen Weigert
这个完美地运行了!快速易懂。 - kyrlon
注意:在bash中,echo "…\n…"无法正常工作。在bash中,你可以使用printf "…\n…" - undefined

2
问题说应该将ExpecV>=CurrV视为成功,但这并没有太多意义(当前版本旧于预期版本可能会导致某些问题),而且在您对此答案的评论中,您暗示期望的行为是相反的,因此这个答案是这样做的。
这需要GNU sort使用其-V选项(版本排序):详情请见版本排序
if cmp -s <(cut -d: -f2 infile) <(cut -d: -f2 infile | sort -V); then
    echo 'FAILURE'
else
    echo 'SUCCESS'
fi

这需要保证带有CurrV的行始终是第一行。使用cut提取冒号后面的部分,并将未排序的结果(第一个进程替换<(...))与版本排序的输出(第二个进程替换)进行比较。

如果它们相同,即第二行的版本大于或等于第一行的版本,cmp的退出状态为成功,我们打印FAILURE;如果它们不同,这意味着sort颠倒了顺序,期望的版本小于当前版本,因此我们打印SUCCESS

-s标志是为了抑制cmp的输出(“silent”);我们只关心退出状态。


如果您已经在单独的变量CurrVExpecV中拥有1.5.21.8.1,则可以按照以下方式执行类似操作:

CurrV='1.5.2'
ExpecV='1.8.1'
printf -v versions '%s\n%s' "$CurrV" "$ExpecV"
if [[ $versions = "$(sort -V <<< "$versions")" ]]; then
    echo 'FAILURE'
else
    echo 'SUCCESS'
fi

这将两个变量存储到versions中,用换行符分隔,然后将未排序的序列与已排序的序列进行比较。


我已经编写了代码从file1中获取ExpecVCurrV - all_techie
@all_techie 进程替换使得一个命令的输出可以像文件一样被使用。你的尝试是将变量的内容作为命令来运行。我的解决方案替换了你的整个尝试,并且操作的是你展示的输入文件。 - Benjamin W.
@all_techie 我添加了一个以两个变量开头的变体。 - Benjamin W.
根据逻辑,脚本应该输出“FAILURE”,因为“Currv”小于“ExpecV”。但是当我运行脚本时,它却显示“SUCCESS”。谢谢。 - all_techie
@all_techie 这个问题的意思是“如果ExpecV>=CurrV,那么我应该输出SUCCESS”。 - Benjamin W.
显示剩余2条评论

1
如果您使用的是Debian系统,则使用dpkg是最简单的方法:
if dpkg --compare-versions $A lt $B
then
  # $A < $B was true
fi

它支持六个比较运算符(参见 man dpkg 并搜索 compare-versions)。
一个潜在的缺点:你的版本必须与 Debian 兼容。

1

@benjamin-w和@Walter A的回答都非常简明扼要。我们也可以使用for循环逐个比较子版本的数值来进行比较:

#!/bin/bash
#
# given 2 version numbers:
# check if ExpecV>=CurrV: SUCCESS
#
CurrV=1.5.2
ExpecV=1.8.1

#
# here below:
#   xarr: array of split CurrV numerical elements
#   yarr: array of split ExpecV numerical elements
#   isnewer: key if version ExpecV is newer than or equal to CurrV
#
#
# use parameter expansion to replace dot by space,
# and then convert them to arrays:
#
xarr=(${CurrV//./ })
yarr=(${ExpecV//./ })
    
#
# suppose that ExpecV is newer (bigger) or equal to CurrV version:
#
isnewer=true

#
# loop over array keys:
#
for i in "${!xarr[@]}"; do
  #
  #printf '${xarr[%s]}=%s\n' $i ${xarr[i]}
  #printf '${yarr[%s]}=%s\n' $i ${yarr[i]}
  #
  #
  # compare sub-version values: break if not equal:
  #
  if [ ${yarr[i]} -gt ${xarr[i]} ]; then
    break
  elif [ ${yarr[i]} -lt ${xarr[i]} ]; then
    isnewer=false
    break
  fi
done

#
# show result:
#
if [ $isnewer == true ]; then
  echo "SUCCESS: $ExpecV is newer than or equal to $CurrV."
else
  echo "FAILURE: $ExpecV is not newer than or equal to $CurrV."
fi

我认为isnewer=false需要在另一个if分支上(我需要更改它才能使其工作:) - Geob-o-matic

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