是否有办法在Bash中比较这样的字符串,例如:2.4.5
和2.8
以及2.4.5.1
?
是否有办法在Bash中比较这样的字符串,例如:2.4.5
和2.8
以及2.4.5.1
?
这里是一个纯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 '>'
运行测试:
$ . ./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'
如果您使用的是Ubuntu Karmic中的coreutils-7(而不是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
由于GNU sort还具有-C
(--check=silent
),它仅报告(通过退出状态)输入是否已排序,因此我们实际上不需要捕获输出并测试它-我们可以编写
verlte() {
printf '%s\n' "$1" "$2" | sort -C -V
}
verlt() {
! verlte "$2" "$1"
}
ver_between() {
# args: min, actual, max
printf '%s\n' "$@" | sort -C -V
}
brew install coreutils
。然后上述内容只需修改以使用gsort即可。 - justseeprintf
比使用echo -e
更好。 - phksort
命令也有 -C
或 --check=silent
选项,因此可以编写 verlte() { printf '%s\n%s' "$1" "$2" | sort -C -V }
来实现。严格小于的检查可以更简单地使用 verlt() { ! verlte "$2" "$1" }
来完成。 - Toby Speight实现这个目标可能没有普遍正确的方法。如果您正在尝试比较Debian软件包系统中的版本,请尝试使用dpkg --compare-versions <first> <relation> <second>
命令。
dpkg --compare-versions "1.0" "lt" "1.2"
表示 1.0 小于 1.2。比较结果 $?
如果为 0
则表示为真,因此您可以在 if
语句之后直接使用它。 - KrisWebDevGNU sort有一个选项可以对版本号进行排序:
printf '2.4.5\n2.8\n2.4.5.1\n' | sort -V
给:
2.4.5
2.4.5.1
2.8
-C
,我们能够比较版本(感谢@WolframRösler的评论)。if ! printf '7.18\n%s\n' "$(curl -V | grep -io "[0-9][0-9a-z.-]*" | head -n1)" | sort -V -C; then
echo "Error: curl version is older than 7.18!"
else
echo "curl version is at least 7.18."
fi
解释:
-C
检查获取的行是否已经排序,如果没有排序则返回退出状态 1(= 版本不相等或较旧)grep -io "[0-9][0-9a-z.-]*" | head -n1
捕获由 command -V
返回的第一个版本字符串,该字符串必须以数字开头,可以后跟字母数字字符、点和连字符,甚至可以包括像 6.2-RC3
这样的版本echo -e "2.4.10\n2.4.9" | sort -n -t.
- kanakasort
命令(http://www.busybox.net/downloads/BusyBox.html#sort)不包含 -V
选项。请注意,该选项很重要。 - Craig McQueenbusybox sort
已经支持选项-V
。 - Bruce-C
来进行比较而不是排序,例如 printf '2.4.5\n2.4.10\n' | sort -V -C
。如果第一个版本号较旧,则输出为空,退出状态为0;如果第一个版本号较新,则退出状态为1。 - Wolfram Röslerfunction 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)
注意:此函数目前支持最多四个组件的版本。如果需要,可以轻松扩展。
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
-t
选项只接受单个字符制表符...否则,2.4-r9
也可以工作。真遗憾 :/ - scottysseusdesired="1.9\n1.11"; actual="$(echo -e $desired |sort -t '.' -k 1,1 -k 2,2 -g)";
,然后验证 if [ "$desired" = "$actual" ]
。 - tresf这个版本最多支持4个字段。
$ function ver { printf "%03d%03d%03d%03d" $(echo "$1" | tr '.' ' '); }
$ [ $(ver 10.9) -lt $(ver 10.10) ] && echo hello
hello
printf "%03d%03d%03d%03d%03d" $(echo "$1" | tr '.' '\n' | head -n 5)
。 - robinsthead -n
起作用,我不得不改成 tr '.' '\n'
。 - Victor Sergienko%03d
调整为%04d
或%02d
。因此,您可以通过%03d%02d%03d
来处理类似于“96.0.4664.45”的Google Chrome版本。 - hh skladbyV
- 一个纯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
<>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
}
第一行:定义本地变量:
a
、op
、b
- 比较操作数和运算符,即"3.6" > "3.5a"。al
、bl
- a
和 b
的字母尾部,初始化为尾部项,即"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
- 用于将零右填充到 ai
和 bi
的变量。首先仅保留 a
和 b
的元素数量相等的各个项目之间的点。第7行:然后在每个点后附加 "0" 以形成填充掩码。
第9行:本地变量:
w
- 项宽度fmt
- printf 格式字符串,将被计算x
- 临时变量IFS=.
命令将变量值在 '.' 处分割。第10行:计算最大项宽度 w,用于对字典序进行比较。在我们的示例中,w=2。
第11行:通过将 $a.$b
的每个字符替换为 %${w}s
来创建 printf 对齐格式,即"3.6" > "3.5a" 会产生 "%2s%2s%2s%2s"。
第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 行:对于 b
,与第 12 行相同。
第 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
.
上进行拆分,并按照以下算法进行比较,该算法取自于这里。如果版本相同,则返回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"
如果只是想知道一个版本是否比另一个低,可以使用命令 sort --version-sort
来检查我的版本字符串的顺序是否更改:
string="$1
$2"
[ "$string" == "$(sort --version-sort <<< "$string")" ]