Jawaban:
Ini adalah versi Bash murni yang tidak memerlukan utilitas eksternal apa pun:
#!/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 '>'
Jalankan tes:
$ . ./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
: / but +1 untuk kode hebat
Jika Anda memiliki coreutils-7 (di Ubuntu Karmic tetapi tidak ceria) maka sort
perintah Anda harus memiliki -V
opsi (versi sortir) yang dapat Anda gunakan untuk melakukan perbandingan:
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
. Maka yang di atas hanya harus dimodifikasi untuk menggunakan gsort.
sort
tidak memiliki -V
opsi.
printf
daripada echo -e
.
sort
juga memiliki -C
atau --check=silent
, sehingga Anda dapat menulis verlte() { printf '%s\n%s' "$1" "$2" | sort -C -V }
; dan memeriksa ketat kurang dari yang lebih sederhana dilakukan sebagai verlt() { ! verlte "$2" "$1" }
.
Mungkin tidak ada cara yang benar secara universal untuk mencapai ini. Jika Anda mencoba membandingkan versi dalam sistem paket Debian, cobalahdpkg --compare-versions <first> <relation> <second>.
dpkg --compare-versions "1.0" "lt" "1.2"
berarti 1,0 kurang dari 1,2. Hasil perbandingan $?
adalah 0
jika benar sehingga Anda dapat menggunakannya langsung setelah if
pernyataan.
GNU sort memiliki opsi untuk itu:
printf '2.4.5\n2.8\n2.4.5.1\n' | sort -V
memberi:
2.4.5
2.4.5.1
2.8
echo -e "2.4.10\n2.4.9" | sort -n -t.
sort
tidak memiliki -V
opsi.
printf '%s\n' "2.4.5" "2.8" "2.4.5.1" | sort -V
.
coreutils 7+
.
Nah jika Anda tahu jumlah bidang yang bisa Anda gunakan -kn, n dan dapatkan solusi super sederhana
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
opsi hanya menerima tab karakter tunggal ... jika tidak, 2.4-r9
akan berfungsi juga. Sayang sekali: /
-g
menjadi -n
. Ada alasan mengapa tidak untuk contoh ini? Sebagai catatan ... untuk melakukan perbandingan jenis "lebih besar dari", Anda dapat memeriksa apakah jenis yang diinginkan sama dengan jenis yang sebenarnya ... misalnya desired="1.9\n1.11"; actual="$(echo -e $desired |sort -t '.' -k 1,1 -k 2,2 -g)";
dan kemudian verifikasi if [ "$desired" = "$actual" ]
.
Ini untuk paling banyak 4 bidang dalam versi.
$ 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
dapat bekerja, saya harus berubah ketr '.' '\n'
tr
Output pipa sed 's/\(^\| \)0\([0-9][0-9]*\)/\1\2/g'
yang akan mengatasi hal itu (Agak canggung)
function version { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; }
Digunakan seperti itu:
if [ $(version $VAR) -ge $(version "6.2.0") ]; then
echo "Version is up to date"
fi
Anda dapat membagi .
dan membandingkan secara rekursif seperti yang ditunjukkan dalam algoritma berikut, diambil dari sini . Ini mengembalikan 10 jika versinya sama, 11 jika versi 1 lebih besar dari versi 2 dan 9 sebaliknya.
#!/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"
Saya menerapkan fungsi yang mengembalikan hasil yang sama seperti Dennis Williamson tetapi menggunakan lebih sedikit garis. Itu memang melakukan pemeriksaan kewarasan pada awalnya yang menyebabkan 1..0
gagal dari tesnya (yang saya berpendapat harus menjadi kasus) tetapi semua tes yang lain lulus dengan kode ini:
#!/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
}
Berikut adalah fungsi Bash sederhana yang tidak menggunakan perintah eksternal. Ini berfungsi untuk string versi yang memiliki hingga tiga bagian numerik di dalamnya - kurang dari 3 juga. Itu dapat dengan mudah diperpanjang untuk lebih. Ini alat =
, <
, <=
, >
, >=
, dan !=
kondisi.
#!/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
}
Inilah tesnya:
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
Subset dari output tes:
<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
- solusi bash murni, tidak ada utilitas eksternal yang diperlukan.=
==
!=
<
<=
>
dan>=
(leksikografis).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
}
Baris 1 : Tentukan variabel lokal:
a
, op
, b
- operan perbandingan dan operator, yaitu, "3.6"> "3.5a".al
, bl
- huruf ekor a
dan b
, diinisialisasi ke item ekor, yaitu, "6" dan "5a".Baris 2, 3 : Digit pangkas kiri dari item ekor sehingga hanya huruf yang tersisa, jika ada, yaitu, "" dan "a".
Baris 4 : Potong kanan surat dari a
dan b
untuk meninggalkan hanya urutan item numerik sebagai variabel lokal ai
danbi
, yaitu, "3,6" dan "3,5". Contoh penting: "4.01-RC2"> "4.01-RC1" menghasilkan ai = "4.01" al = "- RC2" dan bi = "4.01" bl = "- RC1".
Baris 6 : Tentukan variabel lokal:
ap
, bp
- nol bantalan kanan untuk ai
dan bi
. Mulailah dengan menjaga titik antar-item saja, yang jumlahnya sama dengan jumlah elemen a
dan b
masing - masing.Baris 7 : Kemudian tambahkan "0" setelah setiap titik untuk membuat masker padding.
Baris 9 : Variabel lokal:
w
- lebar barangfmt
- string format printf, harus dihitungx
- sementaraIFS=.
bash membagi nilai variabel di '.'Baris 10 : Hitung w
, lebar item maksimum, yang akan digunakan untuk menyelaraskan item untuk perbandingan leksikografis. Dalam contoh kita, w = 2.
Baris 11 : Buat format penyelarasan printf dengan mengganti setiap karakter $a.$b
dengan %${w}s
, yaitu, "3.6"> "3.5a" menghasilkan "% 2s% 2s% 2s% 2s% 2s".
Baris 12 : "printf -v a" menetapkan nilai variabel a
. Ini setara dengan a=sprintf(...)
banyak bahasa pemrograman. Perhatikan bahwa di sini, akibat IFS =. argumen untuk printf
dipecah menjadi item individual.
Dengan printf
item pertama a
diisi dengan spasi sementara cukup "0" item ditambahkan dari bp
untuk memastikan bahwa string yang dihasilkan a
dapat bermakna dibandingkan dengan diformat sama b
.
Perhatikan bahwa kita menambahkan bp
- tidak ap
untuk ai
karena ap
dan bp
mungkin memiliki lenghts berbeda, sehingga hasil ini a
dan b
memiliki panjang yang sama.
Dengan kedua printf
kita tambahkan bagian surat al
untuk a
dengan cukup padding untuk memungkinkan perbandingan yang berarti. Sekarang a
siap untuk perbandingan b
.
Baris 13 : Sama seperti baris 12 tetapi untuk b
.
Baris 15 : Membagi kasus perbandingan antara operator yang tidak built-in ( <=
dan >=
) dan built-in.
Baris 16 : Jika operator pembanding <=
kemudian menguji untuk a<b or a=b
- masing-masing>=
a<b or a=b
Baris 17 : Tes untuk operator perbandingan internal.
<>
# 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
Saya menggunakan Linux tertanam (Yocto) dengan BusyBox. BusyBoxsort
tidak memiliki -V
opsi (tetapi BusyBoxexpr match
dapat melakukan ekspresi reguler). Jadi saya perlu membandingkan versi Bash yang bekerja dengan kendala itu.
Saya telah membuat yang berikut (mirip dengan jawaban Dennis Williamson ) untuk membandingkan menggunakan jenis algoritma "natural sort". Ini membagi string menjadi bagian numerik dan bagian non-numerik; itu membandingkan bagian numerik secara numerik (jadi 10
lebih besar dari 9
), dan membandingkan bagian non-numerik sebagai perbandingan ASCII biasa.
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
}
Itu dapat membandingkan nomor versi yang lebih rumit seperti
1.2-r3
melawan 1.2-r4
1.2rc3
melawan 1.2r4
Perhatikan bahwa itu tidak mengembalikan hasil yang sama untuk beberapa kasus sudut dalam jawaban Dennis Williamson . Khususnya:
1 1.0 <
1.0 1 >
1.0.2.0 1.0.2 >
1..0 1.0 >
1.0 1..0 <
Tapi itu adalah kasus sudut, dan saya pikir hasilnya masih masuk akal.
$ 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
, tanpa perlu test
, seperti ini: if printf '%s\n%s' 4.2.0 "$OVFTOOL_VERSION" | sort --version-sort -C
Ini juga pure bash
solusi, karena printf adalah bash builtin.
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//./ }
}
Untuk versi lama / busybox sort
. Bentuk sederhana memberikan hasil yang kasar dan sering berfungsi.
sort -n
Ini berguna khusus pada versi yang berisi simbol alfa seperti
10.c.3
10.a.4
2.b.5
Bagaimana dengan ini? Tampaknya bekerja?
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"
Berikut ini adalah solusi bash murni lainnya tanpa panggilan eksternal:
#!/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}"
Dan bahkan ada solusi yang lebih sederhana, jika Anda yakin bahwa versi yang dipermasalahkan tidak mengandung nol di depan setelah titik pertama:
#!/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}"
Ini akan bekerja untuk sesuatu seperti 1.2.3 vs 1.3.1 vs 0.9.7, tetapi tidak akan bekerja dengan 1.2.3 vs 1.2.3.0 atau 1.01.1 vs 1.1.1
4.4.4 > 44.3
Berikut adalah penyempurnaan dari jawaban teratas (Dennis's) yang lebih ringkas dan menggunakan skema nilai pengembalian yang berbeda untuk membuatnya mudah diterapkan <= dan> = dengan satu perbandingan. Itu juga membandingkan semuanya setelah karakter pertama tidak dalam [0-9.] Leksikografis, jadi 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
}
Saya menerapkan fungsi pembanding lain. Yang ini memiliki dua persyaratan khusus: (i) Saya tidak ingin fungsi gagal dengan menggunakan return 1
tetapi echo
sebaliknya; (ii) karena kita mengambil versi dari repositori git versi "1.0" harus lebih besar dari "1.0.2", yang berarti bahwa "1.0" berasal dari 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
}
Jangan ragu untuk berkomentar dan menyarankan perbaikan.
Anda dapat menggunakan versi CLI untuk memeriksa batasan versi
$ version ">=1.0, <2.0" "1.7"
$ go version | version ">=1.9"
Contoh skrip 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
Saya menemukan dan memecahkan masalah ini, untuk menambahkan jawaban tambahan (dan lebih pendek dan lebih sederhana) ...
Catatan pertama, perbandingan shell diperpanjang gagal karena Anda mungkin sudah tahu ...
if [[ 1.2.0 < 1.12.12 ]]; then echo true; else echo false; fi
false
Menggunakan sort -t '.'-g (atau sort -V seperti yang disebutkan oleh kanaka) untuk memesan versi dan perbandingan string bash sederhana, saya menemukan solusinya. File input berisi versi dalam kolom 3 dan 4 yang ingin saya bandingkan. Ini berulang melalui daftar mengidentifikasi kecocokan atau jika satu lebih besar dari yang lain. Semoga ini masih dapat membantu orang yang ingin melakukan ini menggunakan bash sesederhana mungkin.
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
Berkat blog Barry untuk ide pengurutan ... 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
Sederhana dan kecil.
echo -ne "$1\n$2"
dengan printf '%s\n ' "$1" "$2"
. Juga lebih baik menggunakan $()
daripada backtics.
Berkat solusi Dennis, kami dapat memperluasnya untuk memungkinkan operator perbandingan '>', '<', '=', '==', '<=', dan '> ='.
# compver ver1 '=|==|>|<|>=|<=' ver2
compver() {
local op
vercomp $1 $3
case $? in
0) op='=';;
1) op='>';;
2) op='<';;
esac
[[ $2 == *$op* ]] && return 0 || return 1
}
Kami kemudian dapat menggunakan operator perbandingan dalam ekspresi seperti:
compver 1.7 '<=' 1.8
compver 1.7 '==' 1.7
compver 1.7 '=' 1.7
dan hanya menguji benar / salah hasilnya, seperti:
if compver $ver1 '>' $ver2; then
echo "Newer"
fi
Ini versi lain dari bash murni, lebih kecil dari pada jawaban yang diterima. Hanya memeriksa apakah suatu versi kurang dari atau sama dengan "versi minimum", dan itu akan memeriksa urutan alfanumerik secara leksikografis, yang sering memberikan hasil yang salah ("snapshot" tidak lebih dari "rilis", untuk memberikan contoh umum) . Ini akan berfungsi dengan baik untuk mayor / minor.
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
}
Pendekatan lain (versi modifikasi dari @joynes) yang membandingkan versi bertitik seperti yang ditanyakan dalam pertanyaan
(yaitu "1.2", "2.3.4", "1.0", "1.10.1", dll.).
Jumlah posisi maksimum harus diketahui sebelumnya. Pendekatan ini mengharapkan posisi versi maksimal 3.
expr $(printf "$1\n$2" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -g | sed -n 2p) != $2
contoh penggunaan:
expr $(printf "1.10.1\n1.7" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -g | sed -n 2p) != "1.7"
pengembalian: 1 sejak 1.10.1 lebih besar dari 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"
pengembalian: 0 sejak 1.10.1 lebih rendah dari 1.11
Inilah solusi Bash murni yang mendukung revisi (mis. '1.0-r1'), berdasarkan jawaban yang diposting oleh Dennis Williamson . Ini dapat dengan mudah dimodifikasi untuk mendukung hal-hal seperti '-RC1' atau mengekstrak versi dari string yang lebih kompleks dengan mengubah ekspresi reguler.
Untuk detail tentang implementasi, silakan merujuk komentar dalam kode dan / atau mengaktifkan kode debug yang disertakan:
#!/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 ... ini jauh dari daftar pertanyaan lama, tapi saya pikir ini adalah jawaban yang cukup elegan. Pertama-tama konversikan setiap versi yang dipisahkan oleh titik menjadi lariknya sendiri, menggunakan ekspansi parameter shell (Lihat Ekspansi Parameter 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//./ })
Sekarang kedua array memiliki nomor versi sebagai string numerik dalam urutan prioritas. Banyak solusi di atas membawa Anda dari sana, tetapi semuanya berasal dari pengamatan bahwa string versi hanyalah bilangan bulat dengan basis sewenang-wenang. Kita dapat menguji menemukan digit tidak sama pertama (seperti strcmp untuk karakter dalam string).
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
}
Ini menggemakan angka negatif jika versi pertama kurang dari yang kedua, nol jika mereka sama dan angka positif jika versi pertama lebih besar. Beberapa output:
$ 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
Kasus degenerasi seperti, ".2" atau "3.0." tidak berfungsi (hasil yang tidak ditentukan), dan jika ada karakter non-numerik di sebelah tanda '.' mungkin gagal (belum diuji) tetapi pasti tidak akan ditentukan. Jadi ini harus dipasangkan dengan fungsi sanitasi atau pemeriksaan yang sesuai untuk pemformatan yang valid. Juga, saya yakin dengan beberapa penyesuaian, ini bisa dibuat lebih kuat tanpa terlalu banyak bagasi tambahan.
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: <
bc
. Ini bukan teks angka.2.1 < 2.10
akan gagal dengan cara ini.