Yanıtlar:
Herhangi bir harici yardımcı program gerektirmeyen saf bir Bash sürümü:
#!/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 '>'
Testleri yapın:
$ . ./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
: / ama büyük kod için +1
Coreutils-7 (Ubuntu Karmic'te ama Jaunty'de değil) varsa, sort
komutunuzda -V
karşılaştırma yapmak için kullanabileceğiniz bir seçenek (sürüm sıralaması) olmalıdır :
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
. O zaman yukarıdakiler sadece gsort kullanacak şekilde değiştirilmelidir.
printf
yerine kullanmak daha iyidir echo -e
.
sort
da vardır, -C
ya --check=silent
da yazabilirsiniz verlte() { printf '%s\n%s' "$1" "$2" | sort -C -V }
; ve daha az sıkı kontrol daha basit olarak yapılır verlt() { ! verlte "$2" "$1" }
.
Bunu başarmanın muhtemelen evrensel olarak doğru bir yolu yoktur. Debian paket sistemindeki sürümleri karşılaştırmaya çalışıyorsanız,dpkg --compare-versions <first> <relation> <second>.
dpkg --compare-versions "1.0" "lt" "1.2"
1.2'den az 1.0 anlamına gelir. Karşılaştırma sonucu $?
olduğunu 0
size hemen sonra kullanabilirsiniz eğer öyleyse gerçek if
ifadesi.
GNU sıralama bunun için bir seçeneğe sahiptir:
printf '2.4.5\n2.8\n2.4.5.1\n' | sort -V
verir:
2.4.5
2.4.5.1
2.8
echo -e "2.4.10\n2.4.9" | sort -n -t.
printf '%s\n' "2.4.5" "2.8" "2.4.5.1" | sort -V
.
coreutils 7+
.
Peki -kn, n kullanabileceğiniz alan sayısını biliyorsanız ve çok basit bir çözüm elde edebilirsiniz
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
seçenek sadece tek karakter sekmelerini kabul eder ... aksi halde 2.4-r9
de işe yarardı. Ne utanç verici: /
-g
için -n
. Bu örnek için neden olmasın? Yan notta ... "büyüktür" türünde bir karşılaştırma yapmak için, istenen sıralamanın gerçek sıralamayla aynı olup olmadığını kontrol edebilirsiniz ... örneğin desired="1.9\n1.11"; actual="$(echo -e $desired |sort -t '.' -k 1,1 -k 2,2 -g)";
ve sonra doğrulayın if [ "$desired" = "$actual" ]
.
Bu sürümdeki en fazla 4 alan içindir.
$ 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
değiştirmek zorunda kaldımtr '.' '\n'
tr
çıkışı sed 's/\(^\| \)0\([0-9][0-9]*\)/\1\2/g'
(Oldukça beceriksiz)
function version { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; }
Gibi kullanılır:
if [ $(version $VAR) -ge $(version "6.2.0") ]; then
echo "Version is up to date"
fi
( https://apple.stackexchange.com/a/123408/11374 adresinden )
Buradan.
alınan aşağıdaki algoritmada gösterildiği gibi tekrar tekrar bölünebilir ve karşılaştırabilirsiniz . Sürümler aynıysa 10, sürüm 1 sürüm 2 ve 9'dan büyükse 11 döndürür.
#!/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"
Dennis Williamson'la aynı sonuçları döndüren ancak daha az satır kullanan bir işlev uyguladım. Bu neden başlangıçta sağlamlık denetimi gerçekleştirmek yapar 1..0
onun testler (Ben iddia ediyorum hangi başarısız gerektiğini durum) ama onun diğer testlerin hepsi bu kodla geçer:
#!/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
}
Harici komutlar kullanmayan basit bir Bash işlevi. İçinde en fazla üç sayısal parça bulunan sürüm dizeleri için çalışır - 3'ten azı da iyidir. Daha fazlası için kolayca genişletilebilir. Bu uygular =
, <
, <=
, >
, >=
, ve !=
koşulları.
#!/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
}
İşte test:
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
Test çıktısının bir alt kümesi:
<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
- saf bash çözümü, harici yardımcı program gerekmez.=
==
!=
<
<=
>
ve >=
(sözlükbilimsel).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
}
Satır 1 : Yerel değişkenleri tanımlayın:
a
, op
, b
- karşılaştırma işlenenler ve operatör, yani "3.6"> "3.5a".al
, bl
- kuyruk öğesinin harf kuyrukları a
ve b
başlangıç değerine, yani "6" ve "5a".Satır 2, 3 : Kuyruk öğelerinden rakamları sola kırpın, böylece sadece harfler kalır, yani "" ve "a".
Satır 4 : Sağ Döşeme harfler gelen a
ve b
yerel değişkenler olarak sayısal öğelerin sadece dizisini bırakmak ai
ve bi
, yani "3.6" ve "3.5". Dikkate değer örnek: "4.01-RC2"> "4.01-RC1", ai = "4.01" al = "- RC2" ve bi = "4.01" bl = "- RC1" verir.
Satır 6 : Yerel değişkenleri tanımlayın:
ap
, bp
- ai
ve için sıfır sağ dolgu bi
. Yalnızca sayı a
ve b
sırasıyla öğe sayısına eşit olan öğeler arası noktaları tutarak başlayın .Satır 7 : Daha sonra dolgu maskeleri yapmak için her noktadan sonra "0" ekleyin.
Satır 9 : Yerel değişkenler:
w
- ürün genişliğifmt
- hesaplanacak printf biçim dizesix
- geçiciIFS=.
Bash ile değişken değerleri '.' Değerine böler.Satır 10 : w
Öğeleri sözlükbilimsel karşılaştırma için hizalamak için kullanılacak maksimum öğe genişliğini hesaplayın . Örneğimizde w = 2.
Satır 11 : Her karakterini "3.6"> "3.5a" $a.$b
ile değiştirerek printf hizalama formatı oluşturun, %${w}s
"% 2s% 2s% 2s% 2s" değerini verir.
Satır 12 : "printf -v a" değişkenin değerini ayarlar a
. Bu, a=sprintf(...)
birçok programlama dilinde eşdeğerdir . Burada IFS = etkisiyle not alın. argümanlarprintf
öğelere ayrılacak .
İlk printf
öğelerinin a
boşluklarla sol dolgulu olmasıyla birlikte bp
, sonuçta elde edilen dizenin a
benzer şekilde biçimlendirilmiş olarak anlamlı bir şekilde karşılaştırılmasını sağlamak için yeterli "0" öğesinin eklenmesib
.
Biz eklemek o Not bp
- değil ap
hiç ai
çünkü ap
ve bp
farklı uzunluklara sahip olabilir, bu sonuçların bu yüzden a
ve b
eşit uzunluklara sahip.
İkinci seçeneği printf
yine harf kısmını eklemek al
için a
anlamlı bir karşılaştırma etkinleştirmek için yeterli dolgu ile. Şimdi a
karşılaştırmaya hazır b
.
Satır 13 : Satır 12 ile aynı, ancak b
.
Satır 15 : Yerleşik olmayan ( <=
ve >=
) ve yerleşik işleçler arasındaki karşılaştırma durumlarını ayırın.
Satır 16 : Karşılaştırma operatörü ise <=
o zaman testi için a<b or a=b
- sırasıyla>=
a<b or a=b
Satır 17 : Yerleşik karşılaştırma işleçlerini test edin.
<>
# 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
BusyBox ile gömülü Linux (Yocto) kullanıyorum. BusyBox'ınsort
bir -V
seçeneği yoktur (ancak BusyBoxexpr match
düzenli ifadeler yapabilir). Bu yüzden, bu kısıtlamayla çalışan bir Bash sürüm karşılaştırmasına ihtiyacım vardı.
"Doğal bir tür" algoritma kullanarak karşılaştırmak için ( Dennis Williamson'ın cevabına benzer) aşağıdakileri yaptım . Dizeyi sayısal parçalara ve sayısal olmayan parçalara böler; sayısal parçaları sayısal olarak karşılaştırır (bu yüzden 10
daha büyüktür 9
) ve sayısal olmayan parçaları düz ASCII karşılaştırması olarak karşılaştırır.
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
}
Gibi daha karmaşık sürüm numaralarını karşılaştırabilir
1.2-r3
karşı 1.2-r4
1.2rc3
karşı 1.2r4
Dennis Williamson'ın cevabındaki bazı köşe vakaları için aynı sonucu döndürmediğini unutmayın . Özellikle:
1 1.0 <
1.0 1 >
1.0.2.0 1.0.2 >
1..0 1.0 >
1.0 1..0 <
Ancak bunlar köşe vakaları ve sonuçların hala makul olduğunu düşünüyorum.
$ 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
gerek olmadan, test
Bu gibi if printf '%s\n%s' 4.2.0 "$OVFTOOL_VERSION" | sort --version-sort -C
Bu aynı zamanda bir pure bash
çözüm, çünkü printf bir bash yerleşiktir.
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//./ }
}
Eski sürüm / meşgul kutusu için sort
. Basit form kabaca sonuç sağlar ve genellikle çalışır.
sort -n
Bu, alfa sembolleri gibi sürümlerde özellikle yararlıdır.
10.c.3
10.a.4
2.b.5
Buna ne dersin? Çalışmak istiyor mu?
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"
İşte harici çağrılar olmadan başka bir saf bash çözümü:
#!/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}"
Ve daha basit bir çözüm var, eğer söz konusu sürümlerin ilk noktadan sonra önde gelen sıfır içermediğinden eminseniz:
#!/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}"
Bu 1.2.3 vs 1.3.1 vs 0.9.7 gibi bir şey için çalışacaktır, ancak 1.2.3 vs 1.2.3.0 veya 1.01.1 vs 1.1.1 ile çalışmaz
4.4.4 > 44.3
Burada, daha özlü olan ve tek bir karşılaştırmayla <= ve> = uygulamasını kolaylaştırmak için farklı bir dönüş değeri şeması kullanan üst yanıtın (Dennis'in) iyileştirilmesi. Ayrıca, sözcüksel olarak [0-9.] 'Da olmayan ilk karakterden sonra her şeyi karşılaştırır, böylece 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
}
Yine başka bir karşılaştırma işlevi uyguladım. Bunun iki özel gereksinimi vardı: (i) fonksiyonun kullanarak başarısız olmasını istemiyordum return 1
ama echo
bunun yerine; (ii) "1.0" git deposu sürümünden sürümler alırken "1.0.2" den daha büyük olmalıdır, yani "1.0" gövdeden gelir.
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
}
Yorum yapmaktan ve iyileştirme önermekten çekinmeyin.
Sürüm kısıtlamalarını kontrol etmek için CLI sürümünü kullanabilirsiniz
$ version ">=1.0, <2.0" "1.7"
$ go version | version ">=1.9"
Bash komut dosyası örneği:
#!/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
Karşılaştım ve ek bir (ve daha kısa ve daha basit) cevap eklemek için bu sorunu çözdüm ...
İlk not, zaten bildiğiniz gibi genişletilmiş kabuk karşılaştırması başarısız oldu ...
if [[ 1.2.0 < 1.12.12 ]]; then echo true; else echo false; fi
false
Sürümleri ve basit bash dize karşılaştırma sipariş sıralamak -t '.'-g (veya kanaka tarafından belirtildiği gibi -V) kullanarak bir çözüm buldum. Giriş dosyası, karşılaştırmak istediğim 3. ve 4. sütunlardaki sürümleri içeriyor. Bu, listeden bir eşleşmeyi tanımlayan veya diğerinden daha büyük olanı tekrarlar. Umarım bu mümkün olduğunca basit olan bash kullanarak bunu yapmak isteyen herkese yardımcı olabilir.
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
Sıralama fikri için Barry'nin bloguna teşekkürler ... 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
Oldukça basit ve küçük.
echo -ne "$1\n$2"
ile printf '%s\n ' "$1" "$2"
. Ayrıca $()
backtics yerine kullanmak daha iyidir.
Dennis'in çözümü sayesinde, '>', '<', '=', '==', '<=' ve '> =' karşılaştırma operatörlerine izin verecek şekilde genişletebiliriz.
# compver ver1 '=|==|>|<|>=|<=' ver2
compver() {
local op
vercomp $1 $3
case $? in
0) op='=';;
1) op='>';;
2) op='<';;
esac
[[ $2 == *$op* ]] && return 0 || return 1
}
Daha sonra aşağıdaki gibi ifadelerde karşılaştırma işleçlerini kullanabiliriz:
compver 1.7 '<=' 1.8
compver 1.7 '==' 1.7
compver 1.7 '=' 1.7
ve sonucun yalnızca doğru / yanlış değerini test edin:
if compver $ver1 '>' $ver2; then
echo "Newer"
fi
İşte başka bir saf bash versiyonu, kabul edilen cevaptan daha küçük. Yalnızca bir sürümün "minimum sürümden" küçük veya ona eşit olup olmadığını kontrol eder ve sık sık yanlış sonuç veren alfa sayısal dizileri kontrol eder ve bu da sık sık yanlış sonuç verir ("anlık görüntü", ortak bir örnek vermek için "sürümden daha geç değildir). . Binbaşı / minör için iyi çalışacaktır.
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
}
Soruda sorulduğu gibi noktalı sürümleri karşılaştıran başka bir yaklaşım (@joynes'in değiştirilmiş sürümü)
(ör. "1.2", "2.3.4", "1.0", "1.10.1" vb.).
Maksimum pozisyon sayısı önceden bilinmelidir. Yaklaşım en fazla 3 sürüm konumu bekler.
expr $(printf "$1\n$2" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -g | sed -n 2p) != $2
örnek kullanım:
expr $(printf "1.10.1\n1.7" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -g | sed -n 2p) != "1.7"
döner: 1.10.1'den beri 1.7 daha büyük
expr $(printf "1.10.1\n1.11" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -g | sed -n 2p) != "1.11"
döndürür: 1.10.1, 1.11'den düşük olduğundan 0
İşte Dennis Williamson tarafından verilen cevaba dayanarak, revizyonları destekleyen saf bir Bash çözümü (örn. '1.0-r1') . '-RC1' gibi şeyleri desteklemek için kolayca değiştirilebilir veya normal ifadeyi değiştirerek sürümü daha karmaşık bir dizeden ayıklayabilir.
Uygulamayla ilgili ayrıntılar için lütfen kod içi yorumlara bakın ve / veya dahil edilen hata ayıklama kodunu etkinleştirin:
#!/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"
Vay be ... bu eski bir soru listesinin aşağısında, ama bence bu oldukça zarif bir cevap. Önce her nokta ile ayrılmış sürümü kabuk parametre genişletmesini kullanarak kendi dizisine dönüştürün (Bkz. Kabuk Parametresi Genişletme ).
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//./ })
Şimdi iki dizi, öncelik sırasına göre sayısal bir dize olarak sürüm numarasına sahiptir. Yukarıdaki çözümlerin birçoğu sizi oradan alır, ancak hepsi sürüm dizesinin sadece keyfi bir tabana sahip bir tamsayı olduğu gözleminden kaynaklanır. İlk eşit olmayan basamağı bulmayı test edebiliriz (strcmp gibi bir dizedeki karakterler için).
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
}
İlk sürüm ikinciden küçükse negatif bir sayı, eşitse sıfır ve ilk sürüm büyükse pozitif bir sayı verir. Bazı çıktılar:
$ 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
".2" veya "3.0" gibi dejenere durumlar. çalışmıyor (tanımlanmamış sonuçlar) ve '.' işaretinin yanında sayısal olmayan karakterler varsa başarısız olabilir (test edilmemiştir), ancak kesinlikle tanımsız olacaktır. Bu nedenle, dezenfeksiyon işlevi veya geçerli biçimlendirme için uygun kontrol ile eşleştirilmelidir. Ayrıca, bazı tweaking ile eminim, bu fazladan fazla bagaj olmadan daha sağlam yapılabilir.
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: <
Kredi @Shellman'a gidiyor
bc
. Metin sayı değil.2.1 < 2.10
bu şekilde başarısız olur.