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


当前回答

这在版本中最多为4个字段。

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

其他回答

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

sort -n

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

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

我实现了另一个比较器函数。这一个有两个特定的要求:(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
}

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

函数V -纯bash解决方案,不需要外部实用程序。 支持== = != < <= >和>=(字典)。 可选尾字母比较:1.5a < 1.5b 长度不等比较:1.6 > 1.5b 从左到右读取:如果V 1.5 '<' 1.6;然后……

<>

# 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

<>

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
}

代码解释

第1行:定义局部变量:

A, op, b -比较操作数和操作符,即"3.6" > "3.5a"。 Al, a和b的bl尾字母,初始化为尾项,即“6”和“5a”。

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

第4行:右修剪a和b中的字母,只保留数字项的序列作为局部变量ai和bi,即“3.6”和“3.5”。 值得注意的例子:"4.01-RC2" > "4.01-RC1"得到ai="4.01" al="-RC2"和bi="4.01" bl="-RC1"。

第6行:定义局部变量:

Ap, bp - 0 ai和bi的右填充。首先只保留项间点,其中点的数量分别等于a和b的元素的数量。

第7行:然后在每个点后面添加“0”来制作填充蒙版。

第9行:局部变量:

W -项目宽度 FMT - printf格式字符串,待计算 X -暂时 IFS =。Bash以“。”分隔变量值。

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

第11行:通过替换$a中的每个字符创建printf对齐格式。w b美元% ${},即,“3.6”>“3.5”收益率“% 2 s % 2 s % 2 s % 2 s”。

第12行:"printf -v a"设置变量a的值。这相当于许多编程语言中的a=sprintf(…)。注意这里,通过IFS=的影响。printf的参数拆分为单独的项。

在第一个printf中,a中的项用空格左填充,同时从bp中追加足够多的“0”项,以确保结果字符串a可以与类似格式的b进行有意义的比较。

请注意,我们将bp -而不是ap附加到ai,因为ap和bp可能有不同的长度,所以这导致a和b具有相同的长度。

对于第二个printf,我们将字母部分al附加到a,并使用足够的填充来进行有意义的比较。现在a可以和b比较了。

第13行:与第12行相同,但b。

第15行:在非内置(<=和>=)操作符和内置操作符之间分割比较情况。

第16行:如果比较操作符<=,则分别测试a<b或a=b - >= a<b或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

如果你有coreutils-7 (Ubuntu Karmic,而不是Jaunty),那么你的排序命令应该有一个-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

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