是否有办法在bash上比较这些字符串,例如:2.4.5和2.8和2.4.5.1?


当前回答

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

其他回答

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

这样用:

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

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

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

    string="$1
$2"
    [ "$string" == "$(sort --version-sort <<< "$string")" ]

下面是对顶部答案(Dennis的)的改进,它更简洁,并使用了不同的返回值方案,以便通过单个比较轻松实现<=和>=。它还比较不是[0-9]的第一个字符之后的所有内容。]因此1.0rc1 < 1.0rc2。

# Compares two tuple-based, dot-delimited version numbers a and b (possibly
# with arbitrary string suffixes). Returns:
# 1 if a<b
# 2 if equal
# 3 if a>b
# Everything after the first character not in [0-9.] is compared
# lexicographically using ASCII ordering if the tuple-based versions are equal.
compare_versions() {
    if [[ $1 == "$2" ]]; then
        return 2
    fi
    local IFS=.
    local i a=(${1%%[^0-9.]*}) b=(${2%%[^0-9.]*})
    local arem=${1#${1%%[^0-9.]*}} brem=${2#${2%%[^0-9.]*}}
    for ((i=0; i<${#a[@]} || i<${#b[@]}; i++)); do
        if ((10#${a[i]:-0} < 10#${b[i]:-0})); then
            return 1
        elif ((10#${a[i]:-0} > 10#${b[i]:-0})); then
            return 3
        fi
    done
    if [ "$arem" '<' "$brem" ]; then
        return 1
    elif [ "$arem" '>' "$brem" ]; then
        return 3
    fi
    return 2
}

为了解决@gammazero的评论,一个(我认为)与语义版本兼容的更长的版本是:

# Compares two dot-delimited decimal-element version numbers a and b that may
# also have arbitrary string suffixes. Compatible with semantic versioning, but
# not as strict: comparisons of non-semver strings may have unexpected
# behavior.
#
# Returns:
# 1 if a<b
# 2 if equal
# 3 if a>b
compare_versions() {
    local LC_ALL=C

    # Optimization
    if [[ $1 == "$2" ]]; then
        return 2
    fi

    # Compare numeric release versions. Supports an arbitrary number of numeric
    # elements (i.e., not just X.Y.Z) in which unspecified indices are regarded
    # as 0.
    local aver=${1%%[^0-9.]*} bver=${2%%[^0-9.]*}
    local arem=${1#$aver} brem=${2#$bver}
    local IFS=.
    local i a=($aver) b=($bver)
    for ((i=0; i<${#a[@]} || i<${#b[@]}; i++)); do
        if ((10#${a[i]:-0} < 10#${b[i]:-0})); then
            return 1
        elif ((10#${a[i]:-0} > 10#${b[i]:-0})); then
            return 3
        fi
    done

    # Remove build metadata before remaining comparison
    arem=${arem%%+*}
    brem=${brem%%+*}

    # Prelease (w/remainder) always older than release (no remainder)
    if [ -n "$arem" -a -z "$brem" ]; then
        return 1
    elif [ -z "$arem" -a -n "$brem" ]; then
        return 3
    fi

    # Otherwise, split by periods and compare individual elements either
    # numerically or lexicographically
    local a=(${arem#-}) b=(${brem#-})
    for ((i=0; i<${#a[@]} && i<${#b[@]}; i++)); do
        local anns=${a[i]#${a[i]%%[^0-9]*}} bnns=${b[i]#${b[i]%%[^0-9]*}}
        if [ -z "$anns$bnns" ]; then
            # Both numeric
            if ((10#${a[i]:-0} < 10#${b[i]:-0})); then
                return 1
            elif ((10#${a[i]:-0} > 10#${b[i]:-0})); then
                return 3
            fi
        elif [ -z "$anns" ]; then
            # Numeric comes before non-numeric
            return 1
        elif [ -z "$bnns" ]; then
            # Numeric comes before non-numeric
            return 3
        else
            # Compare lexicographically
            if [[ ${a[i]} < ${b[i]} ]]; then
                return 1
            elif [[ ${a[i]} > ${b[i]} ]]; then
                return 3
            fi
        fi
    done

    # Fewer elements is earlier
    if (( ${#a[@]} < ${#b[@]} )); then
        return 1
    elif (( ${#a[@]} > ${#b[@]} )); then
        return 3
    fi

    # Must be equal!
    return 2
}

我使用一个函数来规范化这些数字,然后比较它们。

for循环用于将版本字符串中的八进制数转换为十进制数,例如:1.08→1 8,1.0030→1 30,2021-02-03→2021 2 3…

(用bash 5.0.17测试

#!/usr/bin/env bash

v() {
  printf "%04d%04d%04d%04d%04d" $(for i in ${1//[^0-9]/ }; do printf "%d " $((10#$i)); done)
}

while read -r test; do
  set -- $test
  printf "$test    "
  eval "if [[ $(v $1) $3 $(v $2) ]] ; then echo true; else echo false; fi"
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              1                    >
1.2.3~rc2      1.2.3~rc4            >
1.2.3~rc2      1.2.3~rc4           ==
1.2.3~rc2      1.2.3~rc4            <
1.2.3~rc2      1.2.3~rc4           !=
1.2.3~rc2      1.2.3+rc4            <
2021-11-23-rc1 2021-11-23-rc1.1     <
2021-11-23-rc1 2021-11-23-rc1-rf1   <
2021-01-03-rc1 2021-01-04           <
5.0.17(1)-release 5.0.17(2)-release <
EOF

结果:

1              1                   ==    true
2.1            2.2                  <    true
3.0.4.10       3.0.4.2              >    true
4.08           4.08.01              <    true
3.2.1.9.8144   3.2                  >    true
3.2            3.2.1.9.8144         <    true
1.2            2.1                  <    true
2.1            1.2                  >    true
5.6.7          5.6.7               ==    true
1.01.1         1.1.1               ==    true
1.1.1          1.01.1              ==    true
1              1.0                 ==    true
1.0            1                   ==    true
1.0.2.0        1.0.2               ==    true
1..0           1.0                 ==    true
1.0            1..0                ==    true
1              1                    >    false
1.2.3~rc2      1.2.3~rc4            >    false
1.2.3~rc2      1.2.3~rc4           ==    false
1.2.3~rc2      1.2.3~rc4            <    true
1.2.3~rc2      1.2.3~rc4           !=    true
1.2.3~rc2      1.2.3+rc4            <    true
2021-11-23-rc1 2021-11-23-rc1.1     <    true
2021-11-23-rc1 2021-11-23-rc1-rf1   <    true
2021-01-03-rc1 2021-01-04           <    true
5.0.17(1)-release 5.0.17(2)-release <    true

另一种方法(@joynes的修改版本)比较问题中问到的虚线版本 (即“1.2”、“2.3.4”、“1.0”、“1.10.1”等)。 最大数量的位置必须事先知道。该方法期望最多3个版本位置。

expr $(printf "$1\n$2" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -g | sed -n 2p) != $2

使用示例:

expr $(printf "1.10.1\n1.7" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -g | sed -n 2p) != "1.7"

返回:1,因为1.10.1大于1.7

expr $(printf "1.10.1\n1.11" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -g | sed -n 2p) != "1.11"

返回:0,因为1.10.1比1.11低