是否有办法在bash上比较这些字符串,例如:2.4.5和2.8和2.4.5.1?
当前回答
我使用一个函数来规范化这些数字,然后比较它们。
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
其他回答
function version_compare () {
function sub_ver () {
local len=${#1}
temp=${1%%"."*} && indexOf=`echo ${1%%"."*} | echo ${#temp}`
echo -e "${1:0:indexOf}"
}
function cut_dot () {
local offset=${#1}
local length=${#2}
echo -e "${2:((++offset)):length}"
}
if [ -z "$1" ] || [ -z "$2" ]; then
echo "=" && exit 0
fi
local v1=`echo -e "${1}" | tr -d '[[:space:]]'`
local v2=`echo -e "${2}" | tr -d '[[:space:]]'`
local v1_sub=`sub_ver $v1`
local v2_sub=`sub_ver $v2`
if (( v1_sub > v2_sub )); then
echo ">"
elif (( v1_sub < v2_sub )); then
echo "<"
else
version_compare `cut_dot $v1_sub $v1` `cut_dot $v2_sub $v2`
fi
}
### Usage:
version_compare "1.2.3" "1.2.4"
# Output: <
功劳归于@Shellman
下面是一个不需要任何外部工具的纯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'
你可以递归地拆分。和下面的算法进行比较,从这里开始。如果版本相同则返回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"
源
我的观点是:
vercomp () {
if [[ "${1}" == "${2}" ]]; then
echo '0'
return
fi
echo "${1}" | sed 's/\([0-9]\+\)\./\1\n/g' | {
_RES_=-1
for _VB_ in $(echo "${2}" | sed 's/\([0-9]\+\)\./\1\n/g'); do
if ! read -r _VA_ || [[ "${_VB_}" -gt "${_VA_}" ]]; then
_RES_=1
break
fi
done
read -r _VA_ && echo '-1' || echo "${_RES_}"
}
}
语法:
vercomp VERSION_A VERSION_B
打印:
-1如果VERSION_A是最近的版本 如果两个版本相等,则为0 如果VERSION_B是最近的版本,则为1
ver_cmp()
{
local IFS=.
local V1=($1) V2=($2) I
for ((I=0 ; I<${#V1[*]} || I<${#V2[*]} ; I++)) ; do
[[ ${V1[$I]:-0} -lt ${V2[$I]:-0} ]] && echo -1 && return
[[ ${V1[$I]:-0} -gt ${V2[$I]:-0} ]] && echo 1 && return
done
echo 0
}
ver_eq()
{
[[ $(ver_cmp "$1" "$2") -eq 0 ]]
}
ver_lt()
{
[[ $(ver_cmp "$1" "$2") -eq -1 ]]
}
ver_gt()
{
[[ $(ver_cmp "$1" "$2") -eq 1 ]]
}
ver_le()
{
[[ ! $(ver_cmp "$1" "$2") -eq 1 ]]
}
ver_ge()
{
[[ ! $(ver_cmp "$1" "$2") -eq -1 ]]
}
测试:
( ( while read V1 V2 ; do echo $V1 $(ver_cmp $V1 $V2) $V2 ; done ) <<EOF
1.2.3 2.2.3
2.2.3 2.2.2
3.10 3.2
2.2 2.2.1
3.1 3.1.0
EOF
) | sed 's/ -1 / < / ; s/ 0 / = / ; s/ 1 / > /' | column -t
1.2.3 < 2.2.3
2.2.3 > 2.2.2
3.10 > 3.2
2.2 < 2.2.1
3.1 = 3.1.0
ver_lt 10.1.2 10.1.20 && echo 'Your version is too old'
Your version is too old