Antworten:
Hier ist eine reine Bash-Version, für die keine externen Dienstprogramme erforderlich sind:
#!/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 '>'
Führen Sie die Tests aus:
$ . ./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
: / aber +1 für großartigen Code
Wenn Sie coreutils-7 haben (in Ubuntu Karmic, aber nicht Jaunty), sollte Ihr sort
Befehl eine -V
Option (Versionssortierung) haben, mit der Sie den Vergleich durchführen können:
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
. Dann sollte das Obige nur geändert werden, um gsort zu verwenden.
sort
keine -V
Option hat.
printf
als echo -e
.
sort
hat auch -C
oder --check=silent
, damit Sie schreiben können verlte() { printf '%s\n%s' "$1" "$2" | sort -C -V }
; und streng weniger prüfen, als einfacher gemacht wird als verlt() { ! verlte "$2" "$1" }
.
Es gibt wahrscheinlich keinen allgemein korrekten Weg, um dies zu erreichen. Wenn Sie versuchen, Versionen im Debian-Paketsystem zu vergleichen, versuchen Sie esdpkg --compare-versions <first> <relation> <second>.
dpkg --compare-versions "1.0" "lt" "1.2"
bedeutet 1,0 weniger als 1,2. Das Vergleichsergebnis $?
ist 0
if true, sodass Sie es direkt nach der if
Anweisung verwenden können.
Die GNU-Sortierung hat eine Option:
printf '2.4.5\n2.8\n2.4.5.1\n' | sort -V
gibt:
2.4.5
2.4.5.1
2.8
echo -e "2.4.10\n2.4.9" | sort -n -t.
sort
keine -V
Option hat.
printf '%s\n' "2.4.5" "2.8" "2.4.5.1" | sort -V
.
coreutils 7+
.
Wenn Sie die Anzahl der Felder kennen, können Sie -kn, n verwenden und eine supereinfache Lösung erhalten
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
Option akzeptiert nur Registerkarten mit nur einem Zeichen. Andernfalls 2.4-r9
würde dies auch funktionieren. Was für eine Schande: /
-g
zu -n
. Gibt es einen Grund, warum nicht für dieses Beispiel? Nebenbei bemerkt ... um einen Typvergleich "größer als" durchzuführen, können Sie überprüfen, ob die gewünschte Sortierung mit der tatsächlichen Sortierung übereinstimmt ... z . B. desired="1.9\n1.11"; actual="$(echo -e $desired |sort -t '.' -k 1,1 -k 2,2 -g)";
und dann überprüfen if [ "$desired" = "$actual" ]
.
Dies gilt für höchstens 4 Felder in der Version.
$ 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
zu arbeiten, musste ich zutr '.' '\n'
tr
durch sed 's/\(^\| \)0\([0-9][0-9]*\)/\1\2/g'
die das erledigt wird (
function version { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; }
Als solches verwendet:
if [ $(version $VAR) -ge $(version "6.2.0") ]; then
echo "Version is up to date"
fi
Sie können aufgeteilt auf rekursiv .
und vergleichen , wie in den folgenden Algorithmus, aus genommen ist hier . Es gibt 10 zurück, wenn die Versionen identisch sind, 11, wenn Version 1 größer als Version 2 ist, und 9, wenn dies nicht der Fall ist.
#!/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"
Ich habe eine Funktion implementiert, die dieselben Ergebnisse wie Dennis Williamsons zurückgibt, jedoch weniger Zeilen verwendet. Zunächst wird eine Überprüfung der geistigen Gesundheit durchgeführt, die dazu führt 1..0
, dass seine Tests fehlschlagen (was ich argumentieren würde) , soll der Fall sein) , aber alle seine anderen Tests bestehen , mit diesem Code:
#!/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
}
Hier ist eine einfache Bash-Funktion, die keine externen Befehle verwendet. Es funktioniert für Versionszeichenfolgen mit bis zu drei numerischen Teilen - weniger als 3 sind ebenfalls in Ordnung. Es kann leicht für mehr erweitert werden. Es implementiert =
, <
, <=
, >
, >=
, und !=
Bedingungen.
#!/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
}
Hier ist der 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
Eine Teilmenge der Testausgabe:
<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
- reine Bash-Lösung, keine externen Dienstprogramme erforderlich.=
==
!=
<
<=
>
und>=
(lexikografisch).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
}
Zeile 1 : Lokale Variablen definieren:
a
, op
, b
- Vergleichsoperanden und Operator, das heißt, "3.6"> "3.5a".al
, bl
- Buchstabenschwänze von a
und b
, initialisiert auf das Endstück, dh "6" und "5a".Zeilen 2, 3 : Ziffern links von den Endelementen, sodass nur noch Buchstaben übrig sind, z. B. "" und "a".
Zeile 4 : Rechte Trimmbuchstaben von a
und b
, um nur die Folge numerischer Elemente als lokale Variablen zu belassenai
und zubi
dh "3.6" und "3.5" zu belassen. Bemerkenswertes Beispiel: "4.01-RC2"> "4.01-RC1" ergibt ai = "4.01" al = "- RC2" und bi = "4.01" bl = "- RC1".
Zeile 6 : Lokale Variablen definieren:
ap
, bp
- Null Rechtspolster für ai
und bi
. Starten Sie durch die Zwischenelement - Punkte nur zu halten, von welcher Nummer die Anzahl der Elemente gleich a
und b
jeweils.Zeile 7 : Fügen Sie dann nach jedem Punkt "0" hinzu, um Füllmasken zu erstellen.
Zeile 9 : Lokale Variablen:
w
- Artikelbreitefmt
- zu berechnende printf-Formatzeichenfolgex
- vorübergehendIFS=.
Bash werden variable Werte bei '.' Aufgeteilt.Zeile 10 : Berechnen Sie w
die maximale Elementbreite, die zum Ausrichten von Elementen für den lexikografischen Vergleich verwendet wird. In unserem Beispiel ist w = 2.
Zeile 11 : Erstellen der printf Ausrichtungsformat , das von einzelnen Zeichen zu ersetzen , $a.$b
mit %${w}s
, das heißt, "3.6"> "3.5a" Ausbeute "% 2s% 2s% 2s% 2s".
Zeile 12 : "printf -v a" legt den Wert der Variablen fest a
. Dies entspricht a=sprintf(...)
in vielen Programmiersprachen. Beachten Sie, dass hier durch Wirkung von IFS =. die Argumente, printf
die in einzelne Elemente aufgeteilt werden sollen.
Bei den ersten printf
Elementen a
werden links Leerzeichen aufgefüllt, während genügend "0" -Elemente angehängt werden, bp
um sicherzustellen, dass die resultierende Zeichenfolge a
sinnvoll mit einer ähnlich formatierten verglichen werden kann b
.
Beachten Sie, dass wir anhängen bp
- nicht ap
an, ai
weil ap
und bp
möglicherweise unterschiedliche Längen haben, so dass dies zu a
und b
mit gleichen Längen führt.
Mit dem zweiten printf
fügen wir den Brief Teil al
an a
mit genügend Polsterung zu aussagekräftigen Vergleich zu ermöglichen. Jetzt a
ist zum Vergleich bereit mit b
.
Zeile 13 : Wie Zeile 12, jedoch für b
.
Zeile 15 : Aufteilen von Vergleichsfällen zwischen nicht integrierten ( <=
und >=
) und integrierten Operatoren.
Zeile 16 : Wenn der Vergleichsoperator ist, <=
testen Sie auf a<b or a=b
- jeweils>=
a<b or a=b
Zeile 17 : Test für eingebaute Vergleichsoperatoren.
<>
# 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
Ich verwende Embedded Linux (Yocto) mit BusyBox. BusyBoxsort
hat keine -V
Option (aber BusyBoxexpr match
kann reguläre Ausdrücke ausführen ). Also brauchte ich einen Bash-Versionsvergleich, der mit dieser Einschränkung funktionierte.
Ich habe Folgendes gemacht (ähnlich wie Dennis Williamsons Antwort ), um es mit einem Algorithmus vom Typ "natürliche Art" zu vergleichen. Es teilt die Zeichenfolge in numerische Teile und nicht numerische Teile auf. Es vergleicht die numerischen Teile numerisch ( 10
ist also größer als 9
) und vergleicht die nicht numerischen Teile als einfachen ASCII-Vergleich.
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
}
Es kann kompliziertere Versionsnummern wie z
1.2-r3
gegen 1.2-r4
1.2rc3
gegen 1.2r4
Beachten Sie, dass für einige der Eckfälle in Dennis Williamsons Antwort nicht das gleiche Ergebnis zurückgegeben wird . Bestimmtes:
1 1.0 <
1.0 1 >
1.0.2.0 1.0.2 >
1..0 1.0 >
1.0 1..0 <
Aber das sind Eckfälle, und ich denke, die Ergebnisse sind immer noch vernünftig.
$ 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
, ohne dass test
dies erforderlich ist: if printf '%s\n%s' 4.2.0 "$OVFTOOL_VERSION" | sort --version-sort -C
Dies ist auch eine pure bash
Lösung, da printf eine eingebaute Bash ist.
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//./ }
}
Für alte Version / Busybox sort
. Einfache Form liefern ungefähr Ergebnisse und funktionieren oft.
sort -n
Dies ist besonders nützlich bei Versionen, die Alpha-Symbole wie enthalten
10.c.3
10.a.4
2.b.5
Wie wäre es damit? Scheint zu funktionieren?
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"
Hier ist eine weitere reine Bash-Lösung ohne externe Anrufe:
#!/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}"
Und es gibt eine noch einfachere Lösung, wenn Sie sicher sind, dass die fraglichen Versionen keine führenden Nullen nach dem ersten Punkt enthalten:
#!/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}"
Dies funktioniert für etwas wie 1.2.3 gegen 1.3.1 gegen 0.9.7, aber nicht für 1.2.3 gegen 1.2.3.0 oder 1.01.1 gegen 1.1.1
4.4.4 > 44.3
Hier ist eine Verfeinerung der Top-Antwort (Dennis's), die präziser ist und ein anderes Rückgabewertschema verwendet, um die Implementierung von <= und> = mit einem einzigen Vergleich zu vereinfachen. Es vergleicht auch alles nach dem ersten Zeichen nicht in [0-9.] Lexikographisch, also 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
}
Ich habe noch eine weitere Komparatorfunktion implementiert. Dieser hatte zwei spezifische Anforderungen: (i) Ich wollte nicht, dass die Funktion durch Verwendung von return 1
sondern fehlschlägt echo
; (ii) Da wir Versionen aus einem Git-Repository abrufen, sollte "1.0" größer als "1.0.2" sein, was bedeutet, dass "1.0" aus dem Trunk stammt.
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
}
Fühlen Sie sich frei zu kommentieren und Verbesserungen vorzuschlagen.
Sie können die Versions- CLI verwenden, um die Einschränkungen der Version zu überprüfen
$ version ">=1.0, <2.0" "1.7"
$ go version | version ">=1.9"
Beispiel für ein Bash-Skript:
#!/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
Ich bin auf dieses Problem gestoßen und habe es gelöst, um eine zusätzliche (und kürzere und einfachere) Antwort hinzuzufügen ...
Erster Hinweis: Der erweiterte Shell-Vergleich ist fehlgeschlagen, wie Sie vielleicht bereits wissen ...
if [[ 1.2.0 < 1.12.12 ]]; then echo true; else echo false; fi
false
Mit der Sortierung -t '.'- g (oder der Sortierung -V, wie von Kanaka erwähnt) zum Bestellen von Versionen und zum einfachen Vergleich von Bash-Strings habe ich eine Lösung gefunden. Die Eingabedatei enthält Versionen in den Spalten 3 und 4, die ich vergleichen möchte. Dies durchläuft die Liste und identifiziert eine Übereinstimmung oder wenn eine größer als die andere ist. Ich hoffe, dies kann immer noch jedem helfen, der dies mit Bash so einfach wie möglich machen möchte.
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
Vielen Dank an Barrys Blog für die Sortieridee ... 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
Es ist ziemlich einfach und klein.
echo -ne "$1\n$2"
mit printf '%s\n ' "$1" "$2"
. Es ist auch besser, $()
anstelle der Backtics zu verwenden.
Dank Dennis 'Lösung können wir sie erweitern, um Vergleichsoperatoren'> ',' <',' = ',' == ',' <= 'und'> = 'zu ermöglichen.
# compver ver1 '=|==|>|<|>=|<=' ver2
compver() {
local op
vercomp $1 $3
case $? in
0) op='=';;
1) op='>';;
2) op='<';;
esac
[[ $2 == *$op* ]] && return 0 || return 1
}
Wir können dann Vergleichsoperatoren in folgenden Ausdrücken verwenden:
compver 1.7 '<=' 1.8
compver 1.7 '==' 1.7
compver 1.7 '=' 1.7
und testen Sie nur das Richtig / Falsch des Ergebnisses, wie:
if compver $ver1 '>' $ver2; then
echo "Newer"
fi
Hier ist eine weitere reine Bash-Version, die eher kleiner als die akzeptierte Antwort ist. Es wird nur geprüft, ob eine Version kleiner oder gleich einer "Mindestversion" ist, und es werden alphanumerische Sequenzen lexikografisch überprüft, was häufig zu einem falschen Ergebnis führt ("Schnappschuss" ist nicht später als "Veröffentlichung", um ein allgemeines Beispiel zu nennen). . Es wird gut für Dur / Moll funktionieren.
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
}
Ein anderer Ansatz (modifizierte Version von @joynes), der gepunktete Versionen vergleicht, wie in der Frage gestellt
(dh "1.2", "2.3.4", "1.0", "1.10.1" usw.).
Die maximale Anzahl von Positionen muss im Voraus bekannt sein. Der Ansatz erwartet maximal 3 Versionspositionen.
expr $(printf "$1\n$2" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -g | sed -n 2p) != $2
Anwendungsbeispiel:
expr $(printf "1.10.1\n1.7" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -g | sed -n 2p) != "1.7"
Rückgabe: 1, da 1.10.1 größer als 1.7 ist
expr $(printf "1.10.1\n1.11" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -g | sed -n 2p) != "1.11"
Rückgabe: 0, da 1.10.1 niedriger als 1.11 ist
Hier ist eine reine Bash-Lösung, die Revisionen unterstützt (z. B. '1.0-r1'), basierend auf der Antwort von Dennis Williamson . Es kann leicht geändert werden, um Dinge wie '-RC1' zu unterstützen oder die Version aus einer komplexeren Zeichenfolge zu extrahieren, indem der reguläre Ausdruck geändert wird.
Einzelheiten zur Implementierung finden Sie in den In-Code-Kommentaren und / oder aktivieren Sie den enthaltenen Debug-Code:
#!/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 ... das ist ganz unten auf der Liste einer alten Frage, aber ich denke, das ist eine ziemlich elegante Antwort. Konvertieren Sie zuerst jede durch Punkte getrennte Version mithilfe der Shell-Parametererweiterung in ein eigenes Array (siehe Shell-Parametererweiterung ).
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//./ })
Jetzt haben die beiden Arrays die Versionsnummer als numerische Zeichenfolge in Prioritätsreihenfolge. Viele der oben genannten Lösungen führen Sie von dort aus, aber alles ergibt sich aus der Beobachtung, dass die Versionszeichenfolge nur eine Ganzzahl mit einer beliebigen Basis ist. Wir können testen, ob die erste ungleiche Ziffer gefunden wurde (wie es strcmp für Zeichen in einer Zeichenfolge tut).
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
}
Dies gibt eine negative Zahl wieder, wenn die erste Version kleiner als die zweite ist, eine Null, wenn sie gleich sind, und eine positive Zahl, wenn die erste Version größer ist. Einige Ausgaben:
$ 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
Entartete Fälle wie ".2" oder "3.0". funktioniert nicht (undefinierte Ergebnisse) und wenn nicht numerische Zeichen neben dem '.' vorhanden sind. es könnte fehlschlagen (nicht getestet), wird aber sicherlich undefiniert sein. Dies sollte daher mit einer Desinfektionsfunktion oder einer entsprechenden Überprüfung auf gültige Formatierung kombiniert werden. Ich bin mir auch sicher, dass dies mit einigen Optimierungen ohne zu viel zusätzliches Gepäck robuster gemacht werden könnte.
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: <
Das Guthaben geht an @Shellman
bc
. Es ist Text, keine Zahlen.2.1 < 2.10
würde auf diese Weise scheitern.