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

exa*_*che 157 linux versioning bash

有没有办法比较的庆典,如这样的字符串:2.4.52.82.4.5.1

Pau*_*ce. 183

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

#!/bin/bash
vercomp () {
    if [[ $1 == $2 ]]
    then
        return 0
    fi
    local IFS=.
    local i ver1=($1) ver2=($2)
    # 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 $1 $2
    case $? in
        0) op='=';;
        1) op='>';;
        2) op='<';;
    esac
    if [[ $op != $3 ]]
    then
        echo "FAIL: Expected '$3', Actual '$op', Arg1 '$1', Arg2 '$2'"
    else
        echo "Pass: '$1 $op $2'"
    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 Code Online (Sandbox Code Playgroud)

运行测试:

$ . ./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'
Run Code Online (Sandbox Code Playgroud)

  • https://www.gnu.org/licenses/license-list.html#ccbysa`请不要将它用于软件或文档,因为它与GNU GPL`不兼容:/但是代码为+1 (4认同)
  • 您能否明确说明此代码片段的许可?代码看起来很完美,但我不确定是否可以在 AGPLv3 许可项目中使用它。 (3认同)
  • @KamilDziedzic:许可条款在本页底部(以及大多数其他人)中说明. (3认同)
  • 现在看起来不错 我撤回了我的反对意见. (2认同)
  • 这将失败'1.4rc2&gt; 1.3.3'。注意字母数字版本 (2认同)

kan*_*aka 126

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

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

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

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
Run Code Online (Sandbox Code Playgroud)

  • 好的解决方案 对于Mac OSX用户,您可以使用GNU Coreutils gsort.这可以通过自制软件获得:`brew install coreutils`.然后应该修改上面的内容以使用gsort. (5认同)
  • GNU`sort`也具有`-C`或`--check = silent`,因此您可以编写`verlte(){printf'%s \ n%s'“ $ 1”“ $ 2” | 排序-C -V}`; 并严格检查小于,更简单地通过`verlt(){!verlte“ $ 2”“ $ 1”}`。 (4认同)
  • 最好使用`printf`而不是`echo -e`. (3认同)
  • 反引号不是过时的语法吗? (3认同)
  • 在嵌入式Linux系统上不能与例如Busybox一起使用,因为[Busybox`sorting`](http://www.busybox.net/downloads/BusyBox.html#sort)没有`-V`选项. (2认同)

Hel*_*hne 55

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

  • 用法:`dpkg --compare-versions"1.0""lt""1.2"`表示1.0小于1.2.如果为真,比较结果`$?`是'0`,所以你可以在`if`语句之后直接使用它. (5认同)

mou*_*iel 42

GNU sort有一个选项:

printf '2.4.5\n2.8\n2.4.5.1\n' | sort -V
Run Code Online (Sandbox Code Playgroud)

得到:

2.4.5
2.4.5.1
2.8
Run Code Online (Sandbox Code Playgroud)

  • 在嵌入式Linux系统上不能与例如Busybox一起使用,因为[Busybox`sorting`](http://www.busybox.net/downloads/BusyBox.html#sort)没有`-V`选项. (3认同)
  • 问题似乎是关于版本排序.考虑:`echo -e"2.4.10 \n2.4.9"| 排序-n -t (2认同)
  • 用数字排序这是不对的.您至少需要首先规范化字符串. (2认同)
  • 值得注意的是,如果版本号可以是任何值,那么最好以 `printf '%s\n' "2.4.5" "2.8" "2.4.5.1" | 的形式使用它。排序-V`。 (2认同)
  • @CraigMcQueen,最新的“busybox sort”支持选项“-V”。 (2认同)
  • 很好的答案,您可以添加 `-C` 进行比较而不是排序,例如 `printf '2.4.5\n2.4.10\n' | 排序-V -C`。不输出任何内容,如果第一个版本号比第二个版本号旧,则退出状态为 0,如果较新,则退出状态为 1。 (2认同)

joy*_*nes 32

好吧,如果你知道你可以使用的字段数-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
Run Code Online (Sandbox Code Playgroud)

  • 派对迟到了四年,但到目前为止我最喜欢的解决方案:) (4认同)
  • 对于 Solaris 兼容,我必须将 `-g` 更改为 `-n`。对于这个例子,有什么理由不这样做?附带说明...要执行“大于”类型比较,您可以检查所需的排序是否与实际排序相同...例如`desired="1.9\n1.11"; actual="$(echo -e $desired |sort -t '.' -k 1,1 -k 2,2 -g)";` 然后验证`if [ "$desired" = "$actual" ]` . (2认同)

fak*_*ake 20

这适用于版本中最多4个字段.

$ function ver { printf "%03d%03d%03d%03d" $(echo "$1" | tr '.' ' '); }
$ [ $(ver 10.9) -lt $(ver 10.10) ] && echo hello  
hello
Run Code Online (Sandbox Code Playgroud)

  • 如果版本也可以有5个字段,上面可以像这样安全:`printf"%03d%03d%03d%03d"$(echo"$ 1"| tr'.''\n'| head -n 4)` (3认同)
  • 不确定它是否适用于所有版本的bash,但在我的情况下,在最后一轮括号后缺少分号. (2认同)
  • 但它不适用于"10.09". (2认同)

yai*_*chu 13

function version { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; }
Run Code Online (Sandbox Code Playgroud)

用作这样的:

if [ $(version $VAR) -ge $(version "6.2.0") ]; then
    echo "Version is up to date"
fi
Run Code Online (Sandbox Code Playgroud)

(来自https://apple.stackexchange.com/a/123408/11374)

  • 这个优于仅使用上面提出的默认bash printf.它正确处理像"1.09"这样的版本,普通printf无法处理,因为"09不是正确的数字".它还会自动删除前导零,这很有用,因为有时前导零会导致比较错误. (2认同)

dog*_*ane 8

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

#!/bin/bash
do_version_check() {

   [ "$1" == "$2" ] && return 10

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

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

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

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

do_version_check "$1" "$2"
Run Code Online (Sandbox Code Playgroud)

资源


Hac*_*chi 6

如果只是想知道一个版本是否低于另一个版本,我会检查是否sort --version-sort更改了版本字符串的顺序:

    string="$1
$2"
    [ "$string" == "$(sort --version-sort <<< "$string")" ]
Run Code Online (Sandbox Code Playgroud)


Dan*_*Dye 6

$ for OVFTOOL_VERSION in "4.2.0" "4.2.1" "5.2.0" "3.2.0" "4.1.9" "4.0.1" "4.3.0" "4.5.0" "4.2.1" "30.1.0" "4" "5" "4.1" "4.3"
> do
>   if [ $(echo "$OVFTOOL_VERSION 4.2.0" | tr " " "\n" | sort --version-sort | head -n 1) = 4.2.0 ]; then 
>     echo "$OVFTOOL_VERSION is >= 4.2.0"; 
>   else 
>     echo "$OVFTOOL_VERSION is < 4.2.0"; 
>   fi
> done
4.2.0 is >= 4.2.0
4.2.1 is >= 4.2.0
5.2.0 is >= 4.2.0
3.2.0 is < 4.2.0
4.1.9 is < 4.2.0
4.0.1 is < 4.2.0
4.3.0 is >= 4.2.0
4.5.0 is >= 4.2.0
4.2.1 is >= 4.2.0
30.1.0 is >= 4.2.0
4 is < 4.2.0
5 is >= 4.2.0
4.1 is < 4.2.0
4.3 is >= 4.2.0
Run Code Online (Sandbox Code Playgroud)

  • 使用 GNU 排序,您可以使用 `--check=silent`,而不需要 `test`,如下所示: `if printf '%s\n%s' 4.2.0 "$OVFTOOL_VERSION" | 排序--版本排序-C` (3认同)

ste*_*pse 6

  • 功能V- 纯 bash 解决方案,不需要外部实用程序。
  • 支持= == != < <= >>=(字典)。
  • 可选的尾字母比较: 1.5a < 1.5b
  • 不等长比较: 1.6 > 1.5b
  • 从左到右阅读:if V 1.5 '<' 1.6; then ...

<>

# Sample output
# Note: ++ (true) and __ (false) mean that V works correctly.

++ 3.6 '>' 3.5b
__ 2.5.7 '<=' 2.5.6
++ 2.4.10 '<' 2.5.9
__ 3.0002 '>' 3.0003.3
++ 4.0-RC2 '>' 4.0-RC1
Run Code Online (Sandbox Code Playgroud)

<>

function V() # $1-a $2-op $3-$b
# Compare a and b as version strings. Rules:
# R1: a and b : dot-separated sequence of items. Items are numeric. The last item can optionally end with letters, i.e., 2.5 or 2.5a.
# R2: Zeros are automatically inserted to compare the same number of items, i.e., 1.0 < 1.0.1 means 1.0.0 < 1.0.1 => yes.
# R3: op can be '=' '==' '!=' '<' '<=' '>' '>=' (lexicographic).
# R4: Unrestricted number of digits of any item, i.e., 3.0003 > 3.0000004.
# R5: Unrestricted number of items.
{
  local a=$1 op=$2 b=$3 al=${1##*.} bl=${3##*.}
  while [[ $al =~ ^[[:digit:]] ]]; do al=${al:1}; done
  while [[ $bl =~ ^[[:digit:]] ]]; do bl=${bl:1}; done
  local ai=${a%$al} bi=${b%$bl}

  local ap=${ai//[[:digit:]]} bp=${bi//[[:digit:]]}
  ap=${ap//./.0} bp=${bp//./.0}

  local w=1 fmt=$a.$b x IFS=.
  for x in $fmt; do [ ${#x} -gt $w ] && w=${#x}; done
  fmt=${*//[^.]}; fmt=${fmt//./%${w}s}
  printf -v a $fmt $ai$bp; printf -v a "%s-%${w}s" $a $al
  printf -v b $fmt $bi$ap; printf -v b "%s-%${w}s" $b $bl

  case $op in
    '<='|'>=' ) [ "$a" ${op:0:1} "$b" ] || [ "$a" = "$b" ] ;;
    * )         [ "$a" $op "$b" ] ;;
  esac
}
Run Code Online (Sandbox Code Playgroud)

代码解释

第 1 行:定义局部变量:

  • a, op, b- 比较操作数和运算符,即“3.6”>“3.5a”。
  • albl-信尾巴ab,初始化为尾项,即,“6”和“5A”。

第 2、3 行:从尾部项中左修剪数字,因此只剩下字母(如果有),即“”和“a”。

第 4 行:从右侧修剪字母ab仅保留数字项序列作为局部变量aibi,即“3.6”和“3.5”。值得注意的例子:"4.01-RC2" > "4.01-RC1" 产生 ai="4.01" al="-RC2" 和 bi="4.01" bl="-RC1"。

第 6 行:定义局部变量:

  • ap, bp-ai和 的右填充为零bi。通过只保留项目间点,其中数量等于元件的数量开始ab分别。

第 7 行:然后在每个点后附加“0”以制作填充掩码。

第 9 行:局部变量:

  • w - 项目宽度
  • fmt - printf 格式字符串,待计算
  • x - 暂时的
  • 使用IFS=.bash 在 '.' 处拆分变量值。

第 10 行:计算w最大项目宽度,用于对齐项目以进行字典比较。在我们的例子中 w=2。

第 11 行:通过替换$a.$bwith 的每个字符来创建 printf 对齐格式%${w}s,即“3.6”>“3.5a”产生“%2s%2s%2s%2s”。

第 12 行:“printf -v a”设置变量的值a。这相当于a=sprintf(...)在许多编程语言中。注意这里,受 IFS= 的影响。要printf拆分为单个项目的参数。

的第一printfa用空格左填充,同时附加足够的“0”项bp以确保结果字符串a可以与类似格式的b.

请注意,我们添加bp-不apai,因为apbp可能有两种不同长度,所以这个结果ab具有相等的长度。

对于第二个,printf我们将字母部分附加ala足够的填充中以进行有意义的比较。现在a已准备好与b.

第 13 行:与第 12 行相同,但用于b.

第 15 行:拆分非内置 ( <=and >=) 和内置运算符之间的比较案例。

第 16 行:如果比较运算符被分别<=测试为a<b or a=b->= a<b or a=b

第 17 行:测试内置比较运算符。

<>

# All tests

function P { printf "$@"; }
function EXPECT { printf "$@"; }
function CODE { awk $BASH_LINENO'==NR{print " "$2,$3,$4}' "$0"; }
P 'Note: ++ (true) and __ (false) mean that V works correctly.\n'

V 2.5    '!='  2.5      && P + || P _; EXPECT _; CODE
V 2.5    '='   2.5      && P + || P _; EXPECT +; CODE
V 2.5    '=='  2.5      && P + || P _; EXPECT +; CODE

V 2.5a   '=='  2.5b     && P + || P _; EXPECT _; CODE
V 2.5a   '<'   2.5b     && P + || P _; EXPECT +; CODE
V 2.5a   '>'   2.5b     && P + || P _; EXPECT _; CODE
V 2.5b   '>'   2.5a     && P + || P _; EXPECT +; CODE
V 2.5b   '<'   2.5a     && P + || P _; EXPECT _; CODE
V 3.5    '<'   3.5b     && P + || P _; EXPECT +; CODE
V 3.5    '>'   3.5b     && P + || P _; EXPECT _; CODE
V 3.5b   '>'   3.5      && P + || P _; EXPECT +; CODE
V 3.5b   '<'   3.5      && P + || P _; EXPECT _; CODE
V 3.6    '<'   3.5b     && P + || P _; EXPECT _; CODE
V 3.6    '>'   3.5b     && P + || P _; EXPECT +; CODE
V 3.5b   '<'   3.6      && P + || P _; EXPECT +; CODE
V 3.5b   '>'   3.6      && P + || P _; EXPECT _; CODE

V 2.5.7  '<='  2.5.6    && P + || P _; EXPECT _; CODE
V 2.4.10 '<'   2.4.9    && P + || P _; EXPECT _; CODE
V 2.4.10 '<'   2.5.9    && P + || P _; EXPECT +; CODE
V 3.4.10 '<'   2.5.9    && P + || P _; EXPECT _; CODE
V 2.4.8  '>'   2.4.10   && P + || P _; EXPECT _; CODE
V 2.5.6  '<='  2.5.6    && P + || P _; EXPECT +; CODE
V 2.5.6  '>='  2.5.6    && P + || P _; EXPECT +; CODE
V 3.0    '<'   3.0.3    && P + || P _; EXPECT +; CODE
V 3.0002 '<'   3.0003.3 && P + || P _; EXPECT +; CODE
V 3.0002 '>'   3.0003.3 && P + || P _; EXPECT _; CODE
V 3.0003.3 '<' 3.0002   && P + || P _; EXPECT _; CODE
V 3.0003.3 '>' 3.0002   && P + || P _; EXPECT +; CODE

V 4.0-RC2 '>' 4.0-RC1   && P + || P _; EXPECT +; CODE
V 4.0-RC2 '<' 4.0-RC1   && P + || P _; EXPECT _; CODE
Run Code Online (Sandbox Code Playgroud)


v0r*_*tex 5

我实现了一个函数,该函数返回与Dennis Williamson相同的结果,但使用的行数更少。它最初会执行健全性检查,这会导致1..0他的测试失败(我认为应该是这种情况),但他的所有其他测试均通过以下代码通过:

#!/bin/bash
version_compare() {
    if [[ $1 =~ ^([0-9]+\.?)+$ && $2 =~ ^([0-9]+\.?)+$ ]]; then
        local l=(${1//./ }) r=(${2//./ }) s=${#l[@]}; [[ ${#r[@]} -gt ${#l[@]} ]] && s=${#r[@]}

        for i in $(seq 0 $((s - 1))); do
            [[ ${l[$i]} -gt ${r[$i]} ]] && return 1
            [[ ${l[$i]} -lt ${r[$i]} ]] && return 2
        done

        return 0
    else
        echo "Invalid version number given"
        exit 1
    fi
}
Run Code Online (Sandbox Code Playgroud)


cod*_*ter 5

这是一个不使用外部命令的简单 Bash 函数。它适用于最多包含三个数字部分的版本字符串 - 小于 3 也可以。它可以很容易地扩展更多。它实现了=<<=>>=,和!=条件。

#!/bin/bash
vercmp() {
    version1=$1 version2=$2 condition=$3

    IFS=. v1_array=($version1) v2_array=($version2)
    v1=$((v1_array[0] * 100 + v1_array[1] * 10 + v1_array[2]))
    v2=$((v2_array[0] * 100 + v2_array[1] * 10 + v2_array[2]))
    diff=$((v2 - v1))
    [[ $condition = '='  ]] && ((diff == 0)) && return 0
    [[ $condition = '!=' ]] && ((diff != 0)) && return 0
    [[ $condition = '<'  ]] && ((diff >  0)) && return 0
    [[ $condition = '<=' ]] && ((diff >= 0)) && return 0
    [[ $condition = '>'  ]] && ((diff <  0)) && return 0
    [[ $condition = '>=' ]] && ((diff <= 0)) && return 0
    return 1
}
Run Code Online (Sandbox Code Playgroud)

这是测试:

for tv1 in '*' 1.1.1 2.5.3 7.3.0 0.5.7 10.3.9 8.55.32 0.0.1; do
    for tv2 in 3.1.1 1.5.3 4.3.0 0.0.7 0.3.9 11.55.32 10.0.0 '*'; do
      for c in '=' '>' '<' '>=' '<=' '!='; do
        vercmp "$tv1" "$tv2" "$c" && printf '%s\n' "$tv1 $c $tv2 is true" || printf '%s\n' "$tv1 $c $tv2 is false"
      done
    done
done
Run Code Online (Sandbox Code Playgroud)

测试输出的一个子集:

<snip>

* >= * is true
* <= * is true
* != * is true
1.1.1 = 3.1.1 is false
1.1.1 > 3.1.1 is false
1.1.1 < 3.1.1 is true
1.1.1 >= 3.1.1 is false
1.1.1 <= 3.1.1 is true
1.1.1 != 3.1.1 is true
1.1.1 = 1.5.3 is false
1.1.1 > 1.5.3 is false
1.1.1 < 1.5.3 is true
1.1.1 >= 1.5.3 is false
1.1.1 <= 1.5.3 is true
1.1.1 != 1.5.3 is true
1.1.1 = 4.3.0 is false
1.1.1 > 4.3.0 is false

<snip>
Run Code Online (Sandbox Code Playgroud)


Max*_*xim 5

这是一个基于Dennis Williamson 发布的答案的纯 Bash 解决方案,支持修订版(例如“1.0-r1”)。它可以轻松修改以支持“-RC1”之类的内容,或者通过更改正则表达式从更复杂的字符串中提取版本。

有关实现的详细信息,请参阅代码内注释和/或启用包含的调试代码:

#!/bin/bash

# Compare two version strings [$1: version string 1 (v1), $2: version string 2 (v2)]
# Return values:
#   0: v1 == v2
#   1: v1 > v2
#   2: v1 < v2
# Based on: /sf/answers/281754581/ by Dennis Williamson
function compare_versions() {

    # Trivial v1 == v2 test based on string comparison
    [[ "$1" == "$2" ]] && return 0

    # Local variables
    local regex="^(.*)-r([0-9]*)$" va1=() vr1=0 va2=() vr2=0 len i IFS="."

    # Split version strings into arrays, extract trailing revisions
    if [[ "$1" =~ ${regex} ]]; then
        va1=(${BASH_REMATCH[1]})
        [[ -n "${BASH_REMATCH[2]}" ]] && vr1=${BASH_REMATCH[2]}
    else
        va1=($1)
    fi
    if [[ "$2" =~ ${regex} ]]; then
        va2=(${BASH_REMATCH[1]})
        [[ -n "${BASH_REMATCH[2]}" ]] && vr2=${BASH_REMATCH[2]}
    else
        va2=($2)
    fi

    # Bring va1 and va2 to same length by filling empty fields with zeros
    (( ${#va1[@]} > ${#va2[@]} )) && len=${#va1[@]} || len=${#va2[@]}
    for ((i=0; i < len; ++i)); do
        [[ -z "${va1[i]}" ]] && va1[i]="0"
        [[ -z "${va2[i]}" ]] && va2[i]="0"
    done

    # Append revisions, increment length
    va1+=($vr1)
    va2+=($vr2)
    len=$((len+1))

    # *** DEBUG ***
    #echo "TEST: '${va1[@]} (?) ${va2[@]}'"

    # Compare version elements, check if v1 > v2 or v1 < v2
    for ((i=0; i < len; ++i)); do
        if (( 10#${va1[i]} > 10#${va2[i]} )); then
            return 1
        elif (( 10#${va1[i]} < 10#${va2[i]} )); then
            return 2
        fi
    done

    # All elements are equal, thus v1 == v2
    return 0
}


# ---------- everything below this line is just for testing ----------


# Test compare_versions [$1: version string 1, $2: version string 2, $3: expected result]
function test_compare_versions() {
    local op
    compare_versions "$1" "$2"
    case $? in
        0) op="==" ;;
        1) op=">" ;;
        2) op="<" ;;
    esac
    if [[ "$op" == "$3" ]]; then
        echo -e "\e[1;32mPASS: '$1 $op $2'\e[0m"
    else
        echo -e "\e[1;31mFAIL: '$1 $3 $2' (result: '$1 $op $2')\e[0m"
    fi
}

echo -e "\nThe following tests should pass:"
while read -r test; do
    test_compare_versions $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         ==
1.0-r1       1.0-r3       <
1.0-r9       2.0          <
3.0-r15      3.0-r9       >
...-r1       ...-r2       <
2.0-r1       1.9.8.21-r2  >
1.0          3.8.9.32-r   <
-r           -r3          <
-r3          -r           >
-r3          -r3          ==
-r           -r           ==
0.0-r2       0.0.0.0-r2   ==
1.0.0.0-r2   1.0-r2       ==
0.0.0.1-r7   -r9          >
0.0-r0       0            ==
1.002.0-r6   1.2.0-r7     <
001.001-r2   1.1-r2       ==
5.6.1-r0     5.6.1        ==
EOF

echo -e "\nThe following tests should fail:"
while read -r test; do
    test_compare_versions $test
done << EOF
1            1            >
3.0.5-r5     3..5-r5      >
4.9.21-r3    4.8.22-r9    <
1.0-r        1.0-r1       ==
-r           1.0-r        >
-r1          0.0-r1       <
-r2          0-r2         <
EOF
Run Code Online (Sandbox Code Playgroud)


Chr*_*her 5

这也是一个pure bash解决方案,因为 printf 是内置的 bash。

function ver()
# Description: use for comparisons of version strings.
# $1  : a version string of form 1.2.3.4
# use: (( $(ver 1.2.3.4) >= $(ver 1.2.3.3) )) && echo "yes" || echo "no"
{
    printf "%02d%02d%02d%02d" ${1//./ }
}
Run Code Online (Sandbox Code Playgroud)