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


当前回答

与其编写冗长的代码使您的生活过于复杂,不如使用一些已经存在的东西。很多时候,当bash不够用时,python可以提供帮助。你仍然可以很容易地从bash脚本调用它(额外的好处:从bash到python的变量替换):

VERSION1=1.2.3
VERSION2=1.2.4

cat <<EOF | python3 | grep -q True
from packaging import version
print(version.parse("$VERSION1") > version.parse("$VERSION2"))
EOF

if [ "$?" == 0 ];  then
   echo "$VERSION1 is greater than $VERSION2"
else
   echo "$VERSION2 is greater or equal than $VERSION1"
fi

这里有更多信息:如何比较Python中的版本号?

其他回答

我实现了另一个比较器函数。这一个有两个特定的要求:(i)我不希望函数失败使用返回1,但echo代替;(ii)当我们从git存储库中检索版本时,版本“1.0”应该大于“1.0.2”,这意味着“1.0”来自trunk。

function version_compare {
  IFS="." read -a v_a <<< "$1"
  IFS="." read -a v_b <<< "$2"

  while [[ -n "$v_a" || -n "$v_b" ]]; do
    [[ -z "$v_a" || "$v_a" -gt "$v_b" ]] && echo 1 && return
    [[ -z "$v_b" || "$v_b" -gt "$v_a" ]] && echo -1 && return

    v_a=("${v_a[@]:1}")
    v_b=("${v_b[@]:1}")
  done

  echo 0
}

请随意评论并提出改进建议。

我使用嵌入式Linux (Yocto)与BusyBox。BusyBox排序没有-V选项(但BusyBox expr匹配可以做正则表达式)。所以我需要一个Bash版本的比较,它适用于这个约束。

我做了以下(类似于Dennis Williamson的回答)来比较使用“自然排序”类型的算法。它将字符串分成数字部分和非数字部分;它以数字方式比较数字部分(因此10大于9),并以纯ASCII方式比较非数字部分。

ascii_frag() {
    expr match "$1" "\([^[:digit:]]*\)"
}

ascii_remainder() {
    expr match "$1" "[^[:digit:]]*\(.*\)"
}

numeric_frag() {
    expr match "$1" "\([[:digit:]]*\)"
}

numeric_remainder() {
    expr match "$1" "[[:digit:]]*\(.*\)"
}

vercomp_debug() {
    OUT="$1"
    #echo "${OUT}"
}

# return 1 for $1 > $2
# return 2 for $1 < $2
# return 0 for equal
vercomp() {
    local WORK1="$1"
    local WORK2="$2"
    local NUM1="", NUM2="", ASCII1="", ASCII2=""
    while true; do
        vercomp_debug "ASCII compare"
        ASCII1=`ascii_frag "${WORK1}"`
        ASCII2=`ascii_frag "${WORK2}"`
        WORK1=`ascii_remainder "${WORK1}"`
        WORK2=`ascii_remainder "${WORK2}"`
        vercomp_debug "\"${ASCII1}\" remainder \"${WORK1}\""
        vercomp_debug "\"${ASCII2}\" remainder \"${WORK2}\""

        if [ "${ASCII1}" \> "${ASCII2}" ]; then
            vercomp_debug "ascii ${ASCII1} > ${ASCII2}"
            return 1
        elif [ "${ASCII1}" \< "${ASCII2}" ]; then
            vercomp_debug "ascii ${ASCII1} < ${ASCII2}"
            return 2
        fi
        vercomp_debug "--------"

        vercomp_debug "Numeric compare"
        NUM1=`numeric_frag "${WORK1}"`
        NUM2=`numeric_frag "${WORK2}"`
        WORK1=`numeric_remainder "${WORK1}"`
        WORK2=`numeric_remainder "${WORK2}"`
        vercomp_debug "\"${NUM1}\" remainder \"${WORK1}\""
        vercomp_debug "\"${NUM2}\" remainder \"${WORK2}\""

        if [ -z "${NUM1}" -a -z "${NUM2}" ]; then
            vercomp_debug "blank 1 and blank 2 equal"
            return 0
        elif [ -z "${NUM1}" -a -n "${NUM2}" ]; then
            vercomp_debug "blank 1 less than non-blank 2"
            return 2
        elif [ -n "${NUM1}" -a -z "${NUM2}" ]; then
            vercomp_debug "non-blank 1 greater than blank 2"
            return 1
        fi

        if [ "${NUM1}" -gt "${NUM2}" ]; then
            vercomp_debug "num ${NUM1} > ${NUM2}"
            return 1
        elif [ "${NUM1}" -lt "${NUM2}" ]; then
            vercomp_debug "num ${NUM1} < ${NUM2}"
            return 2
        fi
        vercomp_debug "--------"
    done
}

它可以比较更复杂的版本号,例如

1.2-r3和1.2-r4 1.2 r3 vs 1.2r4

请注意,对于Dennis Williamson的回答中的一些极端情况,它不会返回相同的结果。特别是:

1            1.0          <
1.0          1            >
1.0.2.0      1.0.2        >
1..0         1.0          >
1.0          1..0         <

但这些都是极端情况,我认为结果仍然是合理的。

对于旧版本/busybox排序。简单的形式提供了粗略的结果,往往奏效。

sort -n

这是特别有用的版本,其中包含alpha符号,如

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

另一种方法(@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低

你可以递归地拆分。和下面的算法进行比较,从这里开始。如果版本相同则返回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"