Linux 如何在 Bash 中以点分隔版本格式比较两个字符串?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4023830/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 23:46:03  来源:igfitidea点击:

How to compare two strings in dot separated version format in Bash?

linuxbashversioning

提问by exabiche

Is there any way to compare such strings on bash, e.g.: 2.4.5and 2.8and 2.4.5.1?

有什么办法来比较的bash这样的字符串,如:2.4.52.82.4.5.1

采纳答案by Paused until further notice.

Here is a pure Bash version that doesn't require any external utilities:

这是一个不需要任何外部实用程序的纯 Bash 版本:

#!/bin/bash
vercomp () {
    if [[  ==  ]]
    then
        return 0
    fi
    local IFS=.
    local i ver1=() ver2=()
    # fill empty fields in ver1 with zeros
    for ((i=${#ver1[@]}; i<${#ver2[@]}; i++))
    do
        ver1[i]=0
    done
    for ((i=0; i<${#ver1[@]}; i++))
    do
        if [[ -z ${ver2[i]} ]]
        then
            # fill empty fields in ver2 with zeros
            ver2[i]=0
        fi
        if ((10#${ver1[i]} > 10#${ver2[i]}))
        then
            return 1
        fi
        if ((10#${ver1[i]} < 10#${ver2[i]}))
        then
            return 2
        fi
    done
    return 0
}

testvercomp () {
    vercomp  
    case $? in
        0) op='=';;
        1) op='>';;
        2) op='<';;
    esac
    if [[ $op !=  ]]
    then
        echo "FAIL: Expected '', Actual '$op', Arg1 '', Arg2 ''"
    else
        echo "Pass: ' $op '"
    fi
}

# Run tests
# argument table format:
# testarg1   testarg2     expected_relationship
echo "The following tests should pass"
while read -r test
do
    testvercomp $test
done << EOF
1            1            =
2.1          2.2          <
3.0.4.10     3.0.4.2      >
4.08         4.08.01      <
3.2.1.9.8144 3.2          >
3.2          3.2.1.9.8144 <
1.2          2.1          <
2.1          1.2          >
5.6.7        5.6.7        =
1.01.1       1.1.1        =
1.1.1        1.01.1       =
1            1.0          =
1.0          1            =
1.0.2.0      1.0.2        =
1..0         1.0          =
1.0          1..0         =
EOF

echo "The following test should fail (test the tester)"
testvercomp 1 1 '>'

Run the tests:

运行测试:

$ . ./vercomp
The following tests should pass
Pass: '1 = 1'
Pass: '2.1 < 2.2'
Pass: '3.0.4.10 > 3.0.4.2'
Pass: '4.08 < 4.08.01'
Pass: '3.2.1.9.8144 > 3.2'
Pass: '3.2 < 3.2.1.9.8144'
Pass: '1.2 < 2.1'
Pass: '2.1 > 1.2'
Pass: '5.6.7 = 5.6.7'
Pass: '1.01.1 = 1.1.1'
Pass: '1.1.1 = 1.01.1'
Pass: '1 = 1.0'
Pass: '1.0 = 1'
Pass: '1.0.2.0 = 1.0.2'
Pass: '1..0 = 1.0'
Pass: '1.0 = 1..0'
The following test should fail (test the tester)
FAIL: Expected '>', Actual '=', Arg1 '1', Arg2 '1'

回答by Helmut Grohne

There probably is no universally correct way to achieve this. If you are trying to compare versions in the Debian package system try dpkg --compare-versions <first> <relation> <second>.

可能没有普遍正确的方法来实现这一目标。如果您尝试比较 Debian 软件包系统中的版本,请尝试dpkg --compare-versions <first> <relation> <second>.

回答by mouviciel

GNU sorthas an option for it:

GNU sort有一个选项:

printf '2.4.5\n2.8\n2.4.5.1\n' | sort -V

gives:

给出:

2.4.5
2.4.5.1
2.8

回答by dogbane

You can recursively split on .and compare as shown in the following algorithm, taken from here. It returns 10 if the versions are the same, 11 if version 1 is greater than version 2 and 9 otherwise.

您可以递归拆分.和比较,如下面的算法所示,取自此处。如果版本相同,则返回 10,如果版本 1 大于版本 2,则返回 11,否则返回 9。

#!/bin/bash
do_version_check() {

   [ "" == "" ] && return 10

   ver1front=`echo  | cut -d "." -f -1`
   ver1back=`echo  | cut -d "." -f 2-`

   ver2front=`echo  | cut -d "." -f -1`
   ver2back=`echo  | cut -d "." -f 2-`

   if [ "$ver1front" != "" ] || [ "$ver2front" != "" ]; then
       [ "$ver1front" -gt "$ver2front" ] && return 11
       [ "$ver1front" -lt "$ver2front" ] && return 9

       [ "$ver1front" == "" ] || [ -z "$ver1back" ] && ver1back=0
       [ "$ver2front" == "" ] || [ -z "$ver2back" ] && ver2back=0
       do_version_check "$ver1back" "$ver2back"
       return $?
   else
           [ "" -gt "" ] && return 11 || return 9
   fi
}    

do_version_check "" ""

Source

来源

回答by kanaka

If you have coreutils-7 (in Ubuntu Karmic but not Jaunty) then your sortcommand should have a -Voption (version sort) which you could use to do the comparison:

如果你有 coreutils-7(在 Ubuntu Karmic 但不是 Jaunty),那么你的sort命令应该有一个-V选项(版本排序),你可以用它来进行比较:

verlte() {
    [  "" = "`echo -e "\n" | sort -V | head -n1`" ]
}

verlt() {
    [ "" = "" ] && return 1 || verlte  
}

verlte 2.5.7 2.5.6 && echo "yes" || echo "no" # no
verlt 2.4.10 2.4.9 && echo "yes" || echo "no" # no
verlt 2.4.8 2.4.10 && echo "yes" || echo "no" # yes
verlte 2.5.6 2.5.6 && echo "yes" || echo "no" # yes
verlt 2.5.6 2.5.6 && echo "yes" || echo "no" # no

回答by Daniel YC Lin

For old version/busybox sort. Simple form provide roughly result and often works.

对于旧版本/busybox sort。简单的形式提供大致的结果并且经常有效。

sort -n

This is escpecial useful on version which contains alpha symbols like

这对于包含 alpha 符号的版本特别有用,例如

10.c.3
10.a.4
2.b.5

回答by JStrahl

I came across and solved this problem, to add an additional (and shorter and simpler) answer...

我遇到并解决了这个问题,添加了一个额外的(更短更简单)的答案......

First note, extended shell comparison failed as you may already know...

首先请注意,扩展外壳比较失败,因为您可能已经知道...

    if [[ 1.2.0 < 1.12.12 ]]; then echo true; else echo false; fi
    false

Using the sort -t'.'-g (or sort -V as mentioned by kanaka) to order versions and simple bash string comparison I found a solution. The input file contains versions in columns 3 and 4 which I want to compare. This iterates through the list identifying a match or if one is greater than the other. Hope this may still help anyone looking to do this using bash as simple as possible.

使用 sort -t'.'-g (或 kanaka 提到的 sort -V )来订购版本和简单的 bash 字符串比较,我找到了一个解决方案。输入文件包含我要比较的第 3 列和第 4 列中的版本。这将遍历识别匹配项或一个大于另一个的列表。希望这仍然可以帮助任何希望使用 bash 尽可能简单地做到这一点的人。

while read l
do
    #Field 3 contains version on left to compare (change -f3 to required column).
    kf=$(echo $l | cut -d ' ' -f3)
    #Field 4 contains version on right to compare (change -f4 to required column).
    mp=$(echo $l | cut -d ' ' -f4)

    echo 'kf = '$kf
    echo 'mp = '$mp

    #To compare versions m.m.m the two can be listed and sorted with a . separator and the greater version found.
    gv=$(echo -e $kf'\n'$mp | sort -t'.' -g | tail -n 1)

    if [ $kf = $mp ]; then 
        echo 'Match Found: '$l
    elif [ $kf = $gv ]; then
        echo 'Karaf feature file version is greater '$l
    elif [ $mp = $gv ]; then
        echo 'Maven pom file version is greater '$l
   else
       echo 'Comparison error '$l
   fi
done < features_and_pom_versions.tmp.txt

Thanks to Barry's blog for the sort idea... ref: http://bkhome.org/blog/?viewDetailed=02199

感谢 Barry 的博客的排序想法... ref: http://bkhome.org/blog/?viewDetailed=02199

回答by erh

### the answer is does we second argument is higher
function _ver_higher {
        ver=`echo -ne "\n" |sort -Vr |head -n1`
        if [ "" == "" ]; then
                return 1
        elif [ "" == "$ver" ]; then
                return 0
        else
                return 1
        fi
}

if _ver_higher  ; then
        echo higher
else
        echo same or less
fi

It's pretty simple and small.

它非常简单和小巧。

回答by Artieman

How about this? Seems to work?

这个怎么样?似乎有效?

checkVersion() {
subVer1=
subVer2=

[ "$subVer1" == "$subVer2" ] && echo "Version is same"
echo "Version 1 is $subVer1"
testVer1=$subVer1
echo "Test version 1 is $testVer1"
x=0
while [[ $testVer1 != "" ]]
do
  ((x++))
  testVer1=`echo $subVer1|cut -d "." -f $x`
  echo "testVer1 now is $testVer1"
  testVer2=`echo $subVer2|cut -d "." -f $x`
  echo "testVer2 now is $testVer2"
  if [[ $testVer1 -gt $testVer2 ]]
  then
    echo "$ver1 is greater than $ver2"
    break
  elif [[ "$testVer2" -gt "$testVer1" ]]
  then
    echo "$ver2 is greater than $ver1"
    break
  fi
  echo "This is the sub verion for first value $testVer1"
  echo "This is the sub verion for second value $testVer2"
done
}

ver1=
ver2=
checkVersion "$ver1" "$ver2"

回答by joynes

Well if you know the number of fields you can use -k n,n and get a super-simple solution

好吧,如果您知道可以使用 -kn,n 的字段数并获得超级简单的解决方案

echo '2.4.5
2.8
2.4.5.1
2.10.2' | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -k 4,4 -g

2.4.5
2.4.5.1
2.8
2.10.2