Odpowiedzi:
Oto czysta wersja Bash, która nie wymaga żadnych zewnętrznych narzędzi:
#!/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 '>'
Uruchom testy:
$ . ./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
: / ale +1 za świetny kod
Jeśli masz coreutils-7 (w Ubuntu Karmic, ale nie Jaunty), twoje sort
polecenie powinno mieć -V
opcję (sortowanie wersji), której możesz użyć do porównania:
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
. Następnie powyższe należy po prostu zmodyfikować, aby używać gsort.
sort
nie ma -V
opcji.
printf
zamiast echo -e
.
sort
ma również -C
lub --check=silent
, więc możesz pisać verlte() { printf '%s\n%s' "$1" "$2" | sort -C -V }
; i sprawdzanie ścisłe mniej niż jest to prostsze jako verlt() { ! verlte "$2" "$1" }
.
Prawdopodobnie nie ma uniwersalnie poprawnego sposobu, aby to osiągnąć. Jeśli próbujesz porównać wersje w systemie pakietów Debiana, spróbujdpkg --compare-versions <first> <relation> <second>.
dpkg --compare-versions "1.0" "lt" "1.2"
oznacza 1,0 mniej niż 1,2. Wynik porównania $?
jest 0
prawdziwy, więc możesz go użyć bezpośrednio po if
wyrażeniu.
echo -e "2.4.10\n2.4.9" | sort -n -t.
sort
nie ma -V
opcji.
printf '%s\n' "2.4.5" "2.8" "2.4.5.1" | sort -V
.
coreutils 7+
.
Cóż, jeśli znasz liczbę pól, możesz użyć -kn, n i uzyskać super proste rozwiązanie
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
opcja akceptuje tylko pojedyncze tabulatory ... w przeciwnym 2.4-r9
razie działałaby również. Jaka szkoda: /
-g
na -n
. Jakiś powód, dlaczego nie w tym przykładzie? Na marginesie ... aby przeprowadzić porównanie typu „większe niż”, możesz sprawdzić, czy żądane sortowanie jest takie samo jak rzeczywiste sortowanie ... np. desired="1.9\n1.11"; actual="$(echo -e $desired |sort -t '.' -k 1,1 -k 2,2 -g)";
A następnie zweryfikować if [ "$desired" = "$actual" ]
.
Dotyczy to maksymalnie 4 pól w wersji.
$ 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
pracować, musiałem zmienić się natr '.' '\n'
tr
Wyjście rurowe, przez sed 's/\(^\| \)0\([0-9][0-9]*\)/\1\2/g'
które się tym zajmie (raczej niezgrabnie)
function version { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; }
Używany jako taki:
if [ $(version $VAR) -ge $(version "6.2.0") ]; then
echo "Version is up to date"
fi
Możesz rekurencyjnie podzielić .
i porównać, jak pokazano na poniższym algorytmie, zaczerpniętym stąd . Zwraca 10, jeśli wersje są takie same, 11, jeśli wersja 1 jest większa niż wersja 2 i 9 w przeciwnym razie.
#!/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"
Zaimplementowałem funkcję, która zwraca te same wyniki, co Dennis Williamson, ale używa mniej wierszy. Na początku przeprowadza kontrolę poczytalności, co powoduje, 1..0
że jego testy kończą się niepowodzeniem (co moim zdaniem powinno mieć miejsce), ale wszystkie inne testy przechodzą z tym kodem:
#!/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
}
Oto prosta funkcja Bash, która nie używa zewnętrznych poleceń. Działa w przypadku ciągów wersji, które mają do trzech części numerycznych - mniej niż 3 jest również w porządku. Można go łatwo rozszerzyć na więcej. Realizuje =
, <
, <=
, >
, >=
, i !=
warunki.
#!/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
}
Oto 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
Podzbiór wyników testu:
<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
- czyste rozwiązanie bash, bez zewnętrznych narzędzi.=
==
!=
<
<=
>
i >=
(leksykograficzny).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
}
Linia 1 : Zdefiniuj zmienne lokalne:
a
, op
, b
- argumenty porównania i operator, czyli "3.6"> "3.5a".al
, bl
- końcówki liter a
i b
, zainicjowane w pozycji końcowej, tj. „6” i „5a”.Wiersze 2, 3 : Przycinaj w lewo cyfry z elementów końcowych, tak aby pozostały tylko litery, jeśli istnieją, tj. „” I „a”.
Linia 4 : prawy wykończenia listów od a
i b
pozostawić tylko kolejność elementów liczbowych jako zmienne lokalne ai
i bi
, czyli „3.6” i „3.5”. Godny uwagi przykład: "4.01-RC2"> "4.01-RC1" daje ai = "4.01" al = "- RC2" i bi = "4.01" bl = "- RC1".
Wiersz 6 : Zdefiniuj zmienne lokalne:
ap
, bp
- zero dopełnień w prawo dla ai
i bi
. Zacznij od pozostawienia tylko kropek między pozycjami, których liczba jest równa liczbie elementów odpowiednio a
i b
.Linia 7 : Następnie dodaj „0” po każdej kropce, aby utworzyć maski wypełniające.
Wiersz 9 : Zmienne lokalne:
w
- szerokość przedmiotufmt
- ciąg formatu printf do obliczeniax
- tymczasoweIFS=.
bash dzieli wartości zmiennych w „.”.Wiersz 10 : Oblicz w
, maksymalna szerokość elementu, która zostanie użyta do wyrównania elementów w celu porównania leksykograficznego. W naszym przykładzie w = 2.
Linia 11 : Tworzenie formatu printf wyrównania zastępując każdy znak $a.$b
z %${w}s
, czyli "3.6"> "3.5A" plony "% 2s 2s%%% 2s 2s".
Linia 12 : "printf -v a" ustawia wartość zmiennej a
. Jest to odpowiednik a=sprintf(...)
w wielu językach programowania. Zauważ, że tutaj, w wyniku IFS =. argumenty do printf
podzielenia na poszczególne pozycje.
Pierwsze printf
elementy a
są dopełniane spacjami z lewej strony, podczas gdy wystarczająca liczba elementów „0” jest dodawanych z, bp
aby zapewnić, że wynikowy ciąg a
można w znaczący sposób porównać z podobnie sformatowanym b
.
Należy pamiętać, że mamy dołączyć bp
- nie ap
do ai
ponieważ ap
i bp
mogą mieć różne długościach, tak Powoduje a
i b
o równych długościach.
W drugim printf
dodajemy część literową al
do a
z wystarczającym wypełnieniem, aby umożliwić sensowne porównanie. Teraz a
jest gotowy do porównania z b
.
Linia 13 : taka sama jak linia 12, ale dla b
.
Wiersz 15 : Podziel przypadki porównania między operatorami niewbudowanymi ( <=
i >=
) i wbudowanymi.
Wiersz 16 : Jeśli operator porównania jest <=
następnie testowany dla a<b or a=b
- odpowiednio>=
a<b or a=b
Wiersz 17 : Test dla wbudowanych operatorów porównania.
<>
# 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
Używam wbudowanego systemu Linux (Yocto) z BusyBox. BusyBoxsort
nie ma -V
opcji (ale BusyBoxexpr match
może wykonywać wyrażenia regularne). Potrzebowałem więc porównania wersji Bash, które działało z tym ograniczeniem.
Dokonałem następującego (podobnego do odpowiedzi Dennisa Williamsona ) porównania przy użyciu algorytmu „sortowania naturalnego”. Dzieli ciąg na części numeryczne i nienumeryczne; porównuje części numeryczne numerycznie (więc 10
jest większe niż 9
) i porównuje części nienumeryczne jako zwykłe porównanie ASCII.
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
}
Może porównywać bardziej skomplikowane numery wersji, takie jak
1.2-r3
przeciw 1.2-r4
1.2rc3
przeciw 1.2r4
Zauważ, że nie zwraca tego samego wyniku w niektórych przypadkach narożnych w odpowiedzi Dennisa Williamsona . W szczególności:
1 1.0 <
1.0 1 >
1.0.2.0 1.0.2 >
1..0 1.0 >
1.0 1..0 <
Ale to są narożne przypadki i myślę, że wyniki są nadal rozsądne.
$ 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
, bez potrzeby test
, w ten sposób: if printf '%s\n%s' 4.2.0 "$OVFTOOL_VERSION" | sort --version-sort -C
Jest to również pure bash
rozwiązanie, ponieważ printf jest wbudowaną funkcją bash.
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//./ }
}
Dla starej wersji / busybox sort
. Prosta forma daje z grubsza wynik i często działa.
sort -n
Jest to szczególnie przydatne w wersji, która zawiera symbole alfa, takie jak
10.c.3
10.a.4
2.b.5
Co powiesz na to? Wydaje się, że działa?
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"
Oto kolejne czyste rozwiązanie bash bez żadnych połączeń zewnętrznych:
#!/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}"
A rozwiązanie jest jeszcze prostsze, jeśli masz pewność, że omawiane wersje nie zawierają zer wiodących po pierwszej kropce:
#!/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}"
To zadziała dla czegoś takiego jak 1.2.3 vs 1.3.1 vs 0.9.7, ale nie zadziała z 1.2.3 vs 1.2.3.0 lub 1.01.1 vs 1.1.1
4.4.4 > 44.3
Oto udoskonalenie pierwszej odpowiedzi (Dennisa), która jest bardziej zwięzła i wykorzystuje inny schemat wartości zwracanych, aby ułatwić implementację <= i> = za pomocą pojedynczego porównania. Porównuje również wszystko po pierwszym znaku nie w [0-9.] Leksykograficznie, więc 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
}
Zaimplementowałem jeszcze jedną funkcję komparatora. Ten miał dwa szczególne wymagania: (i) nie chciałem, aby funkcja zawiodła przy użyciu, return 1
ale echo
zamiast tego; (ii) gdy pobieramy wersje z repozytorium git, wersja „1.0” powinna być większa niż „1.0.2”, co oznacza, że „1.0” pochodzi z 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
}
Zapraszam do komentowania i sugerowania ulepszeń.
Możesz użyć interfejsu wiersza polecenia wersji, aby sprawdzić ograniczenia wersji
$ version ">=1.0, <2.0" "1.7"
$ go version | version ">=1.9"
Przykład skryptu 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
Natknąłem się i rozwiązałem ten problem, aby dodać dodatkową (i krótszą i prostszą) odpowiedź ...
Pierwsza uwaga, rozszerzone porównanie powłok nie powiodło się, jak być może już wiesz ...
if [[ 1.2.0 < 1.12.12 ]]; then echo true; else echo false; fi
false
Używając sort -t '.'- g (lub sort -V, jak wspomniało kanaka), aby uporządkować wersje i proste porównanie łańcuchów bash, znalazłem rozwiązanie. Plik wejściowy zawiera wersje w kolumnach 3 i 4, które chcę porównać. Powoduje to iterację listy identyfikującej dopasowanie lub czy jeden jest większy od drugiego. Mam nadzieję, że może to nadal pomóc każdemu, kto chce to zrobić przy użyciu tak prostego basha, jak to tylko możliwe.
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
Podziękowania dla bloga Barry'ego za pomysł sortowania ... 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
To całkiem proste i małe.
echo -ne "$1\n$2"
z printf '%s\n ' "$1" "$2"
. Lepiej też używać $()
zamiast backtics.
Dzięki rozwiązaniu Dennisa możemy go rozszerzyć o operatory porównania '>', '<', '=', '==', '<=' i '> ='.
# compver ver1 '=|==|>|<|>=|<=' ver2
compver() {
local op
vercomp $1 $3
case $? in
0) op='=';;
1) op='>';;
2) op='<';;
esac
[[ $2 == *$op* ]] && return 0 || return 1
}
Możemy wtedy użyć operatorów porównania w wyrażeniach takich jak:
compver 1.7 '<=' 1.8
compver 1.7 '==' 1.7
compver 1.7 '=' 1.7
i testuj tylko prawda / fałsz wyniku, na przykład:
if compver $ver1 '>' $ver2; then
echo "Newer"
fi
Oto kolejna czysta wersja basha, raczej mniejsza niż akceptowana odpowiedź. Sprawdza tylko, czy wersja jest mniejsza lub równa „wersji minimalnej” i sprawdza leksykograficznie ciągi alfanumeryczne, co często daje błędny wynik („migawka” nie jest późniejsza niż „wydanie”, aby podać typowy przykład) . Będzie działać dobrze dla dur / moll.
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
}
Inne podejście (zmodyfikowana wersja @joynes), które porównuje wersje z kropkami zgodnie z pytaniem
(np. „1.2”, „2.3.4”, „1.0”, „1.10.1” itp.).
Maksymalna liczba pozycji musi być wcześniej znana. Podejście przewiduje maksymalnie 3 pozycje wersji.
expr $(printf "$1\n$2" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -g | sed -n 2p) != $2
przykład użycia:
expr $(printf "1.10.1\n1.7" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -g | sed -n 2p) != "1.7"
zwraca: 1, ponieważ 1,10.1 jest większe niż 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"
zwraca: 0, ponieważ 1,10,1 jest mniejsze niż 1,11
Oto czyste rozwiązanie Bash, które obsługuje wersje (np. „1.0-r1”), na podstawie odpowiedzi opublikowanej przez Dennisa Williamsona . Można go łatwo zmodyfikować, aby obsługiwał takie rzeczy, jak „-RC1” lub wyodrębnić wersję z bardziej złożonego ciągu, zmieniając wyrażenie regularne.
Aby uzyskać szczegółowe informacje dotyczące implementacji, zapoznaj się z komentarzami w kodzie i / lub włącz dołączony kod debugowania:
#!/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 ... to daleko w dół listy starego pytania, ale myślę, że to całkiem elegancka odpowiedź. Najpierw przekonwertuj każdą wersję rozdzielaną kropkami na własną tablicę, używając rozwijania parametrów powłoki (zobacz Rozszerzanie parametrów powłoki ).
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//./ })
Teraz dwie tablice mają numer wersji jako ciąg liczbowy w kolejności priorytetu. Wiele z powyższych rozwiązań prowadzi cię stamtąd, ale wszystko to wynika z obserwacji, że łańcuch wersji jest po prostu liczbą całkowitą o dowolnej podstawie. Możemy przetestować znalezienie pierwszej nierównej cyfry (tak jak robi to strcmp dla znaków w ciągu).
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
}
Powoduje to powtórzenie liczby ujemnej, jeśli pierwsza wersja jest mniejsza niż druga, zero, jeśli są równe, i liczbę dodatnią, jeśli pierwsza wersja jest większa. Niektóre dane wyjściowe:
$ 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
Zdegenerowane przypadki, takie jak „.2” lub „3.0”. nie działa (niezdefiniowane wyniki) i jeśli obok znaku „.” znajdują się znaki nienumeryczne. może się nie powieść (nie zostało przetestowane), ale z pewnością będzie niezdefiniowane. Dlatego należy to połączyć z funkcją odkażania lub odpowiednim sprawdzeniem poprawności formatowania. Jestem też pewien, że dzięki pewnym poprawkom można to uczynić bardziej wytrzymałym bez zbytniego dodatkowego bagażu.
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: <
Kredyt trafia do @Shellman
bc
. To tekst, a nie liczby.2.1 < 2.10
zawiodłaby w ten sposób.