Câu trả lời:
Đây là phiên bản Bash thuần túy không yêu cầu bất kỳ tiện ích bên ngoài nào:
#!/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 '>'
Chạy thử nghiệm:
$ . ./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'
Please don't use it for software or documentation, since it is incompatible with the GNU GPL
: / nhưng +1 cho mã tuyệt vời
Nếu bạn có coreutils-7 (trong Ubuntu Karmic nhưng không phải Jasyy) thì sort
lệnh của bạn sẽ có một -V
tùy chọn (sắp xếp phiên bản) mà bạn có thể sử dụng để so sánh:
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
brew install coreutils
. Sau đó, nên sửa đổi ở trên để sử dụng gsort.
sort
không có -V
tùy chọn.
printf
thay vì echo -e
.
sort
cũng có -C
hoặc --check=silent
, vì vậy bạn có thể viết verlte() { printf '%s\n%s' "$1" "$2" | sort -C -V }
; và kiểm tra nghiêm ngặt ít hơn là đơn giản hơn được thực hiện như verlt() { ! verlte "$2" "$1" }
.
Có lẽ không có cách chính xác nào để đạt được điều này. Nếu bạn đang cố so sánh các phiên bản trong hệ thống gói Debian, hãy thửdpkg --compare-versions <first> <relation> <second>.
dpkg --compare-versions "1.0" "lt" "1.2"
có nghĩa là 1.0 nhỏ hơn 1.2. Kết quả so sánh $?
là 0
nếu đúng để bạn có thể sử dụng nó trực tiếp sau if
câu lệnh.
echo -e "2.4.10\n2.4.9" | sort -n -t.
sort
không có -V
tùy chọn.
printf '%s\n' "2.4.5" "2.8" "2.4.5.1" | sort -V
.
coreutils 7+
.
Chà nếu bạn biết số lượng trường bạn có thể sử dụng -kn, n và nhận được một giải pháp siêu đơn giản
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
tùy chọn chỉ chấp nhận các tab ký tự đơn ... nếu không, 2.4-r9
cũng sẽ hoạt động. Thật là xấu hổ: /
-g
sang -n
. Bất kỳ lý do tại sao không cho ví dụ này? Trên một ghi chú bên cạnh ... để thực hiện so sánh loại "lớn hơn", bạn có thể kiểm tra xem loại sắp xếp mong muốn có giống với loại thực tế không ... ví dụ desired="1.9\n1.11"; actual="$(echo -e $desired |sort -t '.' -k 1,1 -k 2,2 -g)";
và sau đó xác minh if [ "$desired" = "$actual" ]
.
Điều này là cho tối đa 4 lĩnh vực trong phiên bản.
$ 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" $(echo "$1" | tr '.' '\n' | head -n 4)
head -n
làm việc, tôi phải đổi sangtr '.' '\n'
tr
Đầu ra ống thông qua sed 's/\(^\| \)0\([0-9][0-9]*\)/\1\2/g'
đó sẽ đảm nhiệm việc đó (Khá vụng về)
function version { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; }
Được sử dụng như vậy:
if [ $(version $VAR) -ge $(version "6.2.0") ]; then
echo "Version is up to date"
fi
Bạn có thể phân chia đệ quy .
và so sánh như được hiển thị trong thuật toán sau, được lấy từ đây . Nó trả về 10 nếu các phiên bản giống nhau, 11 nếu phiên bản 1 lớn hơn phiên bản 2 và 9 nếu không.
#!/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"
Tôi đã triển khai một hàm trả về kết quả giống như của Dennis Williamson nhưng sử dụng ít dòng hơn. Nó thực hiện một kiểm tra sự tỉnh táo ban đầu gây ra 1..0
thất bại từ các bài kiểm tra của mình (mà tôi sẽ tranh luận nên là trường hợp) nhưng tất cả các xét nghiệm khác của ông vượt qua với mã này:
#!/bin/bash
version_compare() {
if [[ $1 =~ ^([0-9]+\.?)+$ && $2 =~ ^([0-9]+\.?)+$ ]]; then
local l=(${1//./ }) r=(${2//./ }) s=${#l[@]}; [[ ${#r[@]} -gt ${#l[@]} ]] && s=${#r[@]}
for i in $(seq 0 $((s - 1))); do
[[ ${l[$i]} -gt ${r[$i]} ]] && return 1
[[ ${l[$i]} -lt ${r[$i]} ]] && return 2
done
return 0
else
echo "Invalid version number given"
exit 1
fi
}
Đây là một hàm Bash đơn giản không sử dụng các lệnh bên ngoài. Nó hoạt động cho các chuỗi phiên bản có tối đa ba phần số trong đó - ít hơn 3 cũng tốt. Nó có thể dễ dàng được mở rộng để biết thêm. Nó cụ =
, <
, <=
, >
, >=
, và !=
điều kiện.
#!/bin/bash
vercmp() {
version1=$1 version2=$2 condition=$3
IFS=. v1_array=($version1) v2_array=($version2)
v1=$((v1_array[0] * 100 + v1_array[1] * 10 + v1_array[2]))
v2=$((v2_array[0] * 100 + v2_array[1] * 10 + v2_array[2]))
diff=$((v2 - v1))
[[ $condition = '=' ]] && ((diff == 0)) && return 0
[[ $condition = '!=' ]] && ((diff != 0)) && return 0
[[ $condition = '<' ]] && ((diff > 0)) && return 0
[[ $condition = '<=' ]] && ((diff >= 0)) && return 0
[[ $condition = '>' ]] && ((diff < 0)) && return 0
[[ $condition = '>=' ]] && ((diff <= 0)) && return 0
return 1
}
Đây là bài kiểm tra:
for tv1 in '*' 1.1.1 2.5.3 7.3.0 0.5.7 10.3.9 8.55.32 0.0.1; do
for tv2 in 3.1.1 1.5.3 4.3.0 0.0.7 0.3.9 11.55.32 10.0.0 '*'; do
for c in '=' '>' '<' '>=' '<=' '!='; do
vercmp "$tv1" "$tv2" "$c" && printf '%s\n' "$tv1 $c $tv2 is true" || printf '%s\n' "$tv1 $c $tv2 is false"
done
done
done
Một tập hợp con của đầu ra thử nghiệm:
<snip>
* >= * is true
* <= * is true
* != * is true
1.1.1 = 3.1.1 is false
1.1.1 > 3.1.1 is false
1.1.1 < 3.1.1 is true
1.1.1 >= 3.1.1 is false
1.1.1 <= 3.1.1 is true
1.1.1 != 3.1.1 is true
1.1.1 = 1.5.3 is false
1.1.1 > 1.5.3 is false
1.1.1 < 1.5.3 is true
1.1.1 >= 1.5.3 is false
1.1.1 <= 1.5.3 is true
1.1.1 != 1.5.3 is true
1.1.1 = 4.3.0 is false
1.1.1 > 4.3.0 is false
<snip>
V
- giải pháp bash thuần túy, không cần các tiện ích bên ngoài.=
==
!=
<
<=
>
và>=
(từ điển).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
}
Dòng 1 : Xác định các biến cục bộ:
a
, op
, b
- toán hạng so sánh và điều hành, ví dụ, "3.6"> "3.5a".al
, bl
- đuôi chữ cái a
và b
, được khởi tạo cho mục đuôi, nghĩa là "6" và "5a".Dòng 2, 3 : Các chữ số cắt trái từ các mục đuôi để chỉ còn lại các chữ cái, nếu có, nghĩa là "" và "a".
Dòng 4 : Cắt các chữ cái bên phải từ a
và b
chỉ để lại chuỗi các mục số là biến cục bộ ai
vàbi
, tức là "3.6" và "3.5". Ví dụ đáng chú ý: "4.01-RC2"> "4.01-RC1" mang lại ai = "4.01" al = "- RC2" và bi = "4.01" bl = "- RC1".
Dòng 6 : Xác định các biến cục bộ:
ap
, bp
- không có miếng đệm bên phải cho ai
và bi
. Bắt đầu bằng cách chỉ giữ lại các dấu chấm liên mục, trong đó số lượng bằng số phần tử của a
và b
tương ứng.Dòng 7 : Sau đó nối "0" sau mỗi dấu chấm để tạo mặt nạ đệm.
Dòng 9 : Biến cục bộ:
w
- chiều rộng mụcfmt
- chuỗi định dạng printf, sẽ được tínhx
- tạm thờiIFS=.
bash chia các giá trị biến tại '.'.Dòng 10 : Tính toán w
, chiều rộng mục tối đa, sẽ được sử dụng để căn chỉnh các mục để so sánh từ điển. Trong ví dụ của chúng tôi w = 2.
Dòng 11 : Tạo định dạng căn chỉnh printf bằng cách thay thế từng ký tự $a.$b
bằng %${w}s
, ví dụ: "3.6"> "3.5a" mang lại "% 2s% 2s% 2s% 2s".
Dòng 12 : "printf -v a" đặt giá trị của biến a
. Điều này tương đương với a=sprintf(...)
trong nhiều ngôn ngữ lập trình. Lưu ý rằng ở đây, do ảnh hưởng của IFS =. các đối số để printf
phân chia thành các mục riêng lẻ.
Với các printf
mục đầu tiên a
được đệm trái với khoảng trắng trong khi các mục "0" đủ được thêm vào bp
để đảm bảo rằng chuỗi kết quả a
có thể được so sánh một cách có ý nghĩa với một định dạng tương tự b
.
Lưu ý rằng chúng tôi nối thêm bp
- không ap
phải ai
vì ap
và bp
có thể có chiều dài khác nhau, do đó, điều này dẫn đến a
và b
có độ dài bằng nhau.
Với thứ hai printf
chúng tôi thêm phần thư al
đến a
với đủ đệm để cho phép so sánh có ý nghĩa. Bây giờ a
đã sẵn sàng để so sánh với b
.
Dòng 13 : Tương tự như dòng 12 nhưng cho b
.
Dòng 15 : Phân tách các trường hợp so sánh giữa các toán tử không tích hợp ( <=
và >=
) và các toán tử tích hợp.
Dòng 16 : Nếu toán tử so sánh <=
thì kiểm tra a<b or a=b
- tương ứng>=
a<b or a=b
Dòng 17 : Kiểm tra các toán tử so sánh tích hợp.
<>
# 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
Tôi đang sử dụng Linux nhúng (Yocto) với BusyBox. BusyBoxsort
không có -V
tùy chọn (nhưng BusyBoxexpr match
có thể thực hiện các biểu thức thông thường). Vì vậy, tôi cần một phiên bản Bash so sánh làm việc với ràng buộc đó.
Tôi đã thực hiện như sau (tương tự như câu trả lời của Dennis Williamson ) để so sánh bằng cách sử dụng loại thuật toán "sắp xếp tự nhiên". Nó chia chuỗi thành các phần số và phần không số; nó so sánh các phần số bằng số (vì vậy 10
lớn hơn 9
) và so sánh các phần không phải là số so sánh ASCII đơn giản.
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
}
Nó có thể so sánh các số phiên bản phức tạp hơn như
1.2-r3
đấu với 1.2-r4
1.2rc3
đấu với 1.2r4
Lưu ý rằng nó không trả lại kết quả tương tự cho một số trường hợp góc trong câu trả lời của Dennis Williamson . Đặc biệt:
1 1.0 <
1.0 1 >
1.0.2.0 1.0.2 >
1..0 1.0 >
1.0 1..0 <
Nhưng đó là những trường hợp góc, và tôi nghĩ rằng kết quả vẫn hợp lý.
$ for OVFTOOL_VERSION in "4.2.0" "4.2.1" "5.2.0" "3.2.0" "4.1.9" "4.0.1" "4.3.0" "4.5.0" "4.2.1" "30.1.0" "4" "5" "4.1" "4.3"
> do
> if [ $(echo "$OVFTOOL_VERSION 4.2.0" | tr " " "\n" | sort --version-sort | head -n 1) = 4.2.0 ]; then
> echo "$OVFTOOL_VERSION is >= 4.2.0";
> else
> echo "$OVFTOOL_VERSION is < 4.2.0";
> fi
> done
4.2.0 is >= 4.2.0
4.2.1 is >= 4.2.0
5.2.0 is >= 4.2.0
3.2.0 is < 4.2.0
4.1.9 is < 4.2.0
4.0.1 is < 4.2.0
4.3.0 is >= 4.2.0
4.5.0 is >= 4.2.0
4.2.1 is >= 4.2.0
30.1.0 is >= 4.2.0
4 is < 4.2.0
5 is >= 4.2.0
4.1 is < 4.2.0
4.3 is >= 4.2.0
--check=silent
, không cần test
, như thế này: if printf '%s\n%s' 4.2.0 "$OVFTOOL_VERSION" | sort --version-sort -C
Đây cũng là một pure bash
giải pháp, vì printf là một bash dựng sẵn.
function ver()
# Description: use for comparisons of version strings.
# $1 : a version string of form 1.2.3.4
# use: (( $(ver 1.2.3.4) >= $(ver 1.2.3.3) )) && echo "yes" || echo "no"
{
printf "%02d%02d%02d%02d" ${1//./ }
}
Đối với phiên bản cũ / busybox sort
. Hình thức đơn giản cung cấp kết quả đại khái và thường làm việc.
sort -n
Điều này đặc biệt hữu ích trên phiên bản có chứa các ký hiệu alpha như
10.c.3
10.a.4
2.b.5
Còn cái này thì sao? Có vẻ để làm việc?
checkVersion() {
subVer1=$1
subVer2=$2
[ "$subVer1" == "$subVer2" ] && echo "Version is same"
echo "Version 1 is $subVer1"
testVer1=$subVer1
echo "Test version 1 is $testVer1"
x=0
while [[ $testVer1 != "" ]]
do
((x++))
testVer1=`echo $subVer1|cut -d "." -f $x`
echo "testVer1 now is $testVer1"
testVer2=`echo $subVer2|cut -d "." -f $x`
echo "testVer2 now is $testVer2"
if [[ $testVer1 -gt $testVer2 ]]
then
echo "$ver1 is greater than $ver2"
break
elif [[ "$testVer2" -gt "$testVer1" ]]
then
echo "$ver2 is greater than $ver1"
break
fi
echo "This is the sub verion for first value $testVer1"
echo "This is the sub verion for second value $testVer2"
done
}
ver1=$1
ver2=$2
checkVersion "$ver1" "$ver2"
Đây là một giải pháp bash thuần túy khác mà không có bất kỳ cuộc gọi bên ngoài nào:
#!/bin/bash
function version_compare {
IFS='.' read -ra ver1 <<< "$1"
IFS='.' read -ra ver2 <<< "$2"
[[ ${#ver1[@]} -gt ${#ver2[@]} ]] && till=${#ver1[@]} || till=${#ver2[@]}
for ((i=0; i<${till}; i++)); do
local num1; local num2;
[[ -z ${ver1[i]} ]] && num1=0 || num1=${ver1[i]}
[[ -z ${ver2[i]} ]] && num2=0 || num2=${ver2[i]}
if [[ $num1 -gt $num2 ]]; then
echo ">"; return 0
elif
[[ $num1 -lt $num2 ]]; then
echo "<"; return 0
fi
done
echo "="; return 0
}
echo "${1} $(version_compare "${1}" "${2}") ${2}"
Và thậm chí còn có giải pháp đơn giản hơn, nếu bạn chắc chắn rằng các phiên bản được đề cập không chứa các số 0 đứng đầu sau dấu chấm đầu tiên:
#!/bin/bash
function version_compare {
local ver1=${1//.}
local ver2=${2//.}
if [[ $ver1 -gt $ver2 ]]; then
echo ">"; return 0
elif
[[ $ver1 -lt $ver2 ]]; then
echo "<"; return 0
fi
echo "="; return 0
}
echo "${1} $(version_compare "${1}" "${2}") ${2}"
Điều này sẽ hoạt động cho một cái gì đó như 1.2.3 so với 1.3.1 so với 0.9.7, nhưng sẽ không hoạt động với 1.2.3 so với 1.2.3.0 hoặc 1.01.1 so với 1.1.1
4.4.4 > 44.3
Đây là một sàng lọc của câu trả lời hàng đầu (Dennis) ngắn gọn hơn và sử dụng lược đồ giá trị trả về khác nhau để giúp dễ dàng thực hiện <= và> = với một so sánh duy nhất. Nó cũng so sánh mọi thứ sau ký tự đầu tiên không có trong [0-9.] Theo từ vựng, vì vậy 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
}
Tôi đã thực hiện một chức năng so sánh khác. Điều này có hai yêu cầu cụ thể: (i) Tôi không muốn chức năng bị lỗi bằng cách sử dụng return 1
mà echo
thay vào đó; (ii) khi chúng tôi truy xuất các phiên bản từ phiên bản kho lưu trữ git "1.0" phải lớn hơn "1.0.2", nghĩa là "1.0" xuất phát từ trung kế.
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
}
Hãy bình luận và đề xuất cải tiến.
Bạn có thể sử dụng phiên bản CLI để kiểm tra các ràng buộc của phiên bản
$ version ">=1.0, <2.0" "1.7"
$ go version | version ">=1.9"
Ví dụ về kịch bản Bash:
#!/bin/bash
if `version -b ">=9.0.0" "$(gcc --version)"`; then
echo "gcc version satisfies constraints >=9.0.0"
else
echo "gcc version doesn't satisfies constraints >=9.0.0"
fi
Tôi đã đi qua và giải quyết vấn đề này, để thêm một câu trả lời (và ngắn hơn và đơn giản hơn) ...
Lưu ý đầu tiên, so sánh shell mở rộng đã thất bại vì bạn có thể đã biết ...
if [[ 1.2.0 < 1.12.12 ]]; then echo true; else echo false; fi
false
Sử dụng sort -t '.'- g (hoặc sort -V như được đề cập bởi kanaka) để đặt hàng các phiên bản và so sánh chuỗi bash đơn giản tôi đã tìm thấy một giải pháp. Tệp đầu vào chứa các phiên bản trong cột 3 và 4 mà tôi muốn so sánh. Điều này lặp lại thông qua danh sách xác định một trận đấu hoặc nếu cái này lớn hơn cái kia. Hy vọng điều này vẫn có thể giúp bất cứ ai muốn làm điều này bằng cách sử dụng bash đơn giản nhất có thể.
while read l
do
#Field 3 contains version on left to compare (change -f3 to required column).
kf=$(echo $l | cut -d ' ' -f3)
#Field 4 contains version on right to compare (change -f4 to required column).
mp=$(echo $l | cut -d ' ' -f4)
echo 'kf = '$kf
echo 'mp = '$mp
#To compare versions m.m.m the two can be listed and sorted with a . separator and the greater version found.
gv=$(echo -e $kf'\n'$mp | sort -t'.' -g | tail -n 1)
if [ $kf = $mp ]; then
echo 'Match Found: '$l
elif [ $kf = $gv ]; then
echo 'Karaf feature file version is greater '$l
elif [ $mp = $gv ]; then
echo 'Maven pom file version is greater '$l
else
echo 'Comparison error '$l
fi
done < features_and_pom_versions.tmp.txt
Cảm ơn blog của Barry về ý tưởng sắp xếp ... ref: http://bkhome.org/blog/?viewDetailed=02199
### the answer is does we second argument is higher
function _ver_higher {
ver=`echo -ne "$1\n$2" |sort -Vr |head -n1`
if [ "$2" == "$1" ]; then
return 1
elif [ "$2" == "$ver" ]; then
return 0
else
return 1
fi
}
if _ver_higher $1 $2; then
echo higher
else
echo same or less
fi
Nó khá đơn giản và nhỏ.
echo -ne "$1\n$2"
bằng printf '%s\n ' "$1" "$2"
. Ngoài ra, tốt hơn là sử dụng $()
thay vì phân tích ngược.
Nhờ giải pháp của Dennis, chúng tôi có thể mở rộng nó để cho phép các toán tử so sánh '>', '<', '=', '==', '<=' và '> ='.
# compver ver1 '=|==|>|<|>=|<=' ver2
compver() {
local op
vercomp $1 $3
case $? in
0) op='=';;
1) op='>';;
2) op='<';;
esac
[[ $2 == *$op* ]] && return 0 || return 1
}
Sau đó chúng ta có thể sử dụng các toán tử so sánh trong các biểu thức như:
compver 1.7 '<=' 1.8
compver 1.7 '==' 1.7
compver 1.7 '=' 1.7
và chỉ kiểm tra đúng / sai của kết quả, như:
if compver $ver1 '>' $ver2; then
echo "Newer"
fi
Đây là một phiên bản bash thuần túy khác, nhỏ hơn câu trả lời được chấp nhận. Nó chỉ kiểm tra xem một phiên bản nhỏ hơn hoặc bằng "phiên bản tối thiểu" hay không và nó sẽ kiểm tra các chuỗi chữ số theo từ vựng, thường cho kết quả sai ("ảnh chụp nhanh" không muộn hơn "phát hành", để đưa ra một ví dụ phổ biến) . Nó sẽ hoạt động tốt cho chính / phụ.
is_number() {
case "$BASH_VERSION" in
3.1.*)
PATTERN='\^\[0-9\]+\$'
;;
*)
PATTERN='^[0-9]+$'
;;
esac
[[ "$1" =~ $PATTERN ]]
}
min_version() {
if [[ $# != 2 ]]
then
echo "Usage: min_version current minimum"
return
fi
A="${1%%.*}"
B="${2%%.*}"
if [[ "$A" != "$1" && "$B" != "$2" && "$A" == "$B" ]]
then
min_version "${1#*.}" "${2#*.}"
else
if is_number "$A" && is_number "$B"
then
[[ "$A" -ge "$B" ]]
else
[[ ! "$A" < "$B" ]]
fi
fi
}
Một cách tiếp cận khác (phiên bản sửa đổi của @joynes) so sánh các phiên bản chấm như được hỏi trong câu hỏi
(ví dụ: "1.2", "2.3.4", "1.0", "1.10.1", v.v.).
Số lượng vị trí tối đa phải được biết trước. Cách tiếp cận mong đợi tối đa 3 vị trí phiên bản.
expr $(printf "$1\n$2" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -g | sed -n 2p) != $2
sử dụng ví dụ:
expr $(printf "1.10.1\n1.7" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -g | sed -n 2p) != "1.7"
trả về: 1 kể từ 1.10.1 lớn hơn 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"
trả về: 0 vì 1.10.1 thấp hơn 1.11
Đây là một giải pháp Bash thuần túy hỗ trợ sửa đổi (ví dụ: '1.0-r1'), dựa trên câu trả lời được đăng bởi Dennis Williamson . Nó có thể dễ dàng được sửa đổi để hỗ trợ các công cụ như '-RC1' hoặc trích xuất phiên bản từ một chuỗi phức tạp hơn bằng cách thay đổi biểu thức thông thường.
Để biết chi tiết về việc triển khai, vui lòng tham khảo các nhận xét trong mã và / hoặc kích hoạt mã gỡ lỗi được bao gồm:
#!/bin/bash
# Compare two version strings [$1: version string 1 (v1), $2: version string 2 (v2)]
# Return values:
# 0: v1 == v2
# 1: v1 > v2
# 2: v1 < v2
# Based on: https://stackoverflow.com/a/4025065 by Dennis Williamson
function compare_versions() {
# Trivial v1 == v2 test based on string comparison
[[ "$1" == "$2" ]] && return 0
# Local variables
local regex="^(.*)-r([0-9]*)$" va1=() vr1=0 va2=() vr2=0 len i IFS="."
# Split version strings into arrays, extract trailing revisions
if [[ "$1" =~ ${regex} ]]; then
va1=(${BASH_REMATCH[1]})
[[ -n "${BASH_REMATCH[2]}" ]] && vr1=${BASH_REMATCH[2]}
else
va1=($1)
fi
if [[ "$2" =~ ${regex} ]]; then
va2=(${BASH_REMATCH[1]})
[[ -n "${BASH_REMATCH[2]}" ]] && vr2=${BASH_REMATCH[2]}
else
va2=($2)
fi
# Bring va1 and va2 to same length by filling empty fields with zeros
(( ${#va1[@]} > ${#va2[@]} )) && len=${#va1[@]} || len=${#va2[@]}
for ((i=0; i < len; ++i)); do
[[ -z "${va1[i]}" ]] && va1[i]="0"
[[ -z "${va2[i]}" ]] && va2[i]="0"
done
# Append revisions, increment length
va1+=($vr1)
va2+=($vr2)
len=$((len+1))
# *** DEBUG ***
#echo "TEST: '${va1[@]} (?) ${va2[@]}'"
# Compare version elements, check if v1 > v2 or v1 < v2
for ((i=0; i < len; ++i)); do
if (( 10#${va1[i]} > 10#${va2[i]} )); then
return 1
elif (( 10#${va1[i]} < 10#${va2[i]} )); then
return 2
fi
done
# All elements are equal, thus v1 == v2
return 0
}
# Test compare_versions [$1: version string 1, $2: version string 2, $3: expected result]
function test_compare_versions() {
local op
compare_versions "$1" "$2"
case $? in
0) op="==" ;;
1) op=">" ;;
2) op="<" ;;
esac
if [[ "$op" == "$3" ]]; then
echo -e "\e[1;32mPASS: '$1 $op $2'\e[0m"
else
echo -e "\e[1;31mFAIL: '$1 $3 $2' (result: '$1 $op $2')\e[0m"
fi
}
echo -e "\nThe following tests should pass:"
while read -r test; do
test_compare_versions $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 ==
1.0-r1 1.0-r3 <
1.0-r9 2.0 <
3.0-r15 3.0-r9 >
...-r1 ...-r2 <
2.0-r1 1.9.8.21-r2 >
1.0 3.8.9.32-r <
-r -r3 <
-r3 -r >
-r3 -r3 ==
-r -r ==
0.0-r2 0.0.0.0-r2 ==
1.0.0.0-r2 1.0-r2 ==
0.0.0.1-r7 -r9 >
0.0-r0 0 ==
1.002.0-r6 1.2.0-r7 <
001.001-r2 1.1-r2 ==
5.6.1-r0 5.6.1 ==
EOF
echo -e "\nThe following tests should fail:"
while read -r test; do
test_compare_versions $test
done << EOF
1 1 >
3.0.5-r5 3..5-r5 >
4.9.21-r3 4.8.22-r9 <
1.0-r 1.0-r1 ==
-r 1.0-r >
-r1 0.0-r1 <
-r2 0-r2 <
EOF
echo -e "\nThe following line should be empty (local variables test):"
echo "$op $regex $va1 $vr1 $va2 $vr2 $len $i $IFS"
Wow ... đây là danh sách của một câu hỏi cũ, nhưng tôi nghĩ đây là một câu trả lời khá thanh lịch. Đầu tiên chuyển đổi từng phiên bản được phân tách bằng dấu chấm thành mảng riêng, sử dụng mở rộng tham số shell (Xem Mở rộng tham số Shell ).
v1="05.2.3" # some evil examples that work here
v2="7.001.0.0"
declare -a v1_array=(${v1//./ })
declare -a v2_array=(${v2//./ })
Bây giờ hai mảng có số phiên bản là một chuỗi số theo thứ tự ưu tiên. Rất nhiều giải pháp trên đưa bạn từ đó, nhưng tất cả đều xuất phát từ quan sát rằng chuỗi phiên bản chỉ là một số nguyên với một cơ sở tùy ý. Chúng ta có thể kiểm tra việc tìm chữ số không đều đầu tiên (như strcmp làm cho các ký tự trong chuỗi).
compare_version() {
declare -a v1_array=(${1//./ })
declare -a v2_array=(${2//./ })
while [[ -nz $v1_array ]] || [[ -nz $v2_array ]]; do
let v1_val=${v1_array:-0} # this will remove any leading zeros
let v2_val=${v2_array:-0}
let result=$((v1_val-v2_val))
if (( result != 0 )); then
echo $result
return
fi
v1_array=("${v1_array[@]:1}") # trim off the first "digit". it doesn't help
v2_array=("${v2_array[@]:1}")
done
# if we get here, both the arrays are empty and neither has been numerically
# different, which is equivalent to the two versions being equal
echo 0
return
}
Điều này lặp lại một số âm nếu phiên bản đầu tiên nhỏ hơn số thứ hai, số 0 nếu chúng bằng nhau và số dương nếu phiên bản đầu tiên lớn hơn. Một số đầu ra:
$ compare_version 1 1.2
-2
$ compare_version "05.1.3" "5.001.03.0.0.0.1"
-1
$ compare_version "05.1.3" "5.001.03.0.0.0"
0
$ compare_version "05.1.3" "5.001.03.0"
0
$ compare_version "05.1.3" "5.001.30.0"
-27
$ compare_version "05.2.3" "7.001.0.0"
-2
$ compare_version "05.1.3" "5.001.30.0"
-27
$ compare_version "7.001.0.0" "05.1.3"
2
Các trường hợp thoái hóa như, ".2" hoặc "3.0." không hoạt động (kết quả không xác định) và nếu các ký tự không phải là số xuất hiện bên cạnh '.' nó có thể thất bại (chưa được thử nghiệm) nhưng chắc chắn sẽ không được xác định. Vì vậy, điều này nên được kết hợp với chức năng khử trùng hoặc kiểm tra thích hợp cho định dạng hợp lệ. Ngoài ra, tôi chắc chắn với một số điều chỉnh, điều này có thể được thực hiện mạnh mẽ hơn mà không cần thêm quá nhiều hành lý.
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: <
Tín dụng vào @Shellman
bc
. Đó là văn bản không phải là số.2.1 < 2.10
sẽ thất bại theo cách này.