it-swarm.com.de

Wie vergleiche ich zwei Strings im punktgetrennten Versionsformat in Bash?

Gibt es eine Möglichkeit, solche Zeichenfolgen in der Bash zu vergleichen, z. B. 2.4.5 und 2.8 und 2.4.5.1?

143
exabiche

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 durch:

$ . ./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'
167

Wenn Sie coreutils-7 (in Ubuntu Karmic, aber nicht Jaunty) haben, 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
116
kanaka

Es gibt wahrscheinlich keinen allgemein korrekten Weg, um dies zu erreichen. Wenn Sie versuchen, Versionen im Debian-Paketsystem zu vergleichen, versuchen Sie dpkg --compare-versions <first> <relation> <second>..

53
Helmut Grohne

GNU sort hat eine Option dafür:

printf '2.4.5\n2.8\n2.4.5.1\n' | sort -V

gibt:

2.4.5
2.4.5.1
2.8
39
mouviciel

Wenn Sie die Anzahl der Felder kennen, können Sie -k n, n verwenden und erhalten eine sehr einfache Lösung

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
29
joynes

Dies gilt für maximal 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
16
fakedrake
function version { echo "[email protected]" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; }

Als solche verwendet:

if [ $(version $VAR) -ge $(version "6.2.0") ]; then
    echo "Version is up to date"
fi

(von https://Apple.stackexchange.com/a/123408/11374 )

10
yairchu

Sie können rekursiv auf . aufteilen und vergleichen, wie im folgenden Algorithmus gezeigt, aus hier . Wenn die Versionen identisch sind, wird 10 zurückgegeben, andernfalls Version 11, wenn Version 1 größer als Version 2 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"

Quelle

7
dogbane

Ich habe eine Funktion implementiert, die dieselben Ergebnisse wie Dennis Williamson liefert, aber weniger Zeilen verwendet. Es führt zunächst eine Überprüfung der Funktionsfähigkeit durch, was dazu führt, dass 1..0 bei seinen Tests fehlschlägt (was ich behaupten würde sollte sein), aber alle 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
}
4
v0rtex

wenn es nur um zu wissen ist, ob eine Version niedriger ist als eine andere, habe ich geprüft, ob sort --version-sort die Reihenfolge meiner Versionszeichenfolgen ändert:

    string="$1
$2"
    [ "$string" == "$(sort --version-sort <<< "$string")" ]
4
Hachi

Ich verwende Embedded Linux (Yocto) mit BusyBox. BusyBox sort hat keine -V-Option (aber BusyBox expr match kann reguläre Ausdrücke ausführen). Also brauchte ich einen Vergleich der Bash-Version, der mit dieser Einschränkung funktionierte.

Ich habe Folgendes gemacht (vergleichbar mit Dennis Williamson's answer ), um einen Algorithmus der "natürlichen Sortierung" zu vergleichen. Die Zeichenfolge wird in numerische und nicht numerische Teile aufgeteilt. Er vergleicht die numerischen Teile numerisch (dh 10 ist größer als 9), und vergleicht die nicht numerischen Teile als Vergleich mit 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
}

Es kann kompliziertere Versionsnummern wie vergleichen

  • 1.2-r3 versus 1.2-r4
  • 1.2rc3 versus 1.2r4

Beachten Sie, dass es für einige der Eckfälle in die Antwort von Dennis Williamson nicht dasselbe Ergebnis liefert. Im Speziellen:

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.

4
Craig McQueen
$ 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
3
Dan Dye
  • Funktion V - reine Bash-Lösung, keine externen Dienstprogramme erforderlich.
  • Unterstützt ===!=<<=> und >= (lexikografisch).
  • Optionaler Briefvergleich: 1.5a < 1.5b
  • Vergleich der Längenunterschiede: 1.6 > 1.5b
  • Liest von links nach rechts: 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
}

Code erklärt

Zeile 1 : Lokale Variablen definieren:

  • a, op, b - Vergleichsoperanden und Operator, d. h. "3.6"> "3.5a".
  • al, bl - Buchstabenschwänze von a und b, initialisiert auf den Endgegenstand, d. h. "6" und "5a".

Zeilen 2, 3 : Linkszuschneidende Ziffern von den Endstücken, so dass nur noch Buchstaben übrig bleiben, d. H. "" Und "a".

Zeile 4 : Rechte Beschneidungsbuchstaben von a und b, um nur die Reihenfolge der numerischen Elemente als lokale Variablen ai und bi zu belassen, dh "3.6" und "3.5" . 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 - keine Rechtsauffüllung für ai und bi. Beginnen Sie, indem Sie nur die Punkte zwischen den Elementen beibehalten, deren Anzahl der Anzahl der Elemente von a und b entspricht.

Zeile 7 : Hängen Sie nach jedem Punkt "0" an, um Füllmasken zu erstellen.

Zeile 9 : Lokale Variablen:

  • w - Artikelbreite
  • fmt - printf-Formatzeichenfolge, die berechnet werden soll
  • x - temporär
  • Mit IFS=. bash teilt die Variablenwerte bei '.'.

Zeile 10 : Berechne w, die maximale Elementbreite, die zum Ausrichten von Elementen für den lexikographischen Vergleich verwendet wird. In unserem Beispiel ist w = 2.

Zeile 11 : Erstellen Sie das printf-Ausrichtungsformat, indem Sie jedes Zeichen von $a.$b durch %${w}s ersetzen, d. H. "3.6"> "3.5a" führt zu "% 2s% 2s% 2s% 2s". 

Zeile 12 : "printf -v a" setzt den Wert der Variablen a. Dies entspricht a=sprintf(...) in vielen Programmiersprachen. Beachten Sie, dass hier durch Wirkung von IFS =. Die Argumente für printf werden in einzelne Elemente aufgeteilt.

Bei den ersten printf-Elementen von a werden Links mit Leerzeichen aufgefüllt, während genügend "0" -Elemente an bp angehängt werden, um sicherzustellen, dass der resultierende String a mit einem ähnlich formatierten b sinnvoll verglichen werden kann.

Beachten Sie, dass wir bp - nicht ap an ai anhängen, da ap und bp unterschiedliche Längen haben können, so dass a und b gleiche Längen haben.

Mit der zweiten printf fügen wir den Buchstabenteil al an a mit genügend Auffüllung an, um einen sinnvollen Vergleich zu ermöglichen. Nun ist a zum Vergleich mit b bereit.

Zeile 13 : Entspricht der Zeile 12, jedoch für b.

Zeile 15 : Split Vergleichsfälle zwischen nicht eingebauten (<= und >=) und integrierten Operatoren.

Zeile 16 : Wenn der Vergleichsoperator <= ist, dann teste auf a<b or a=b - bzw. >=a<b or a=b

Zeile 17 : Test für integrierte Vergleichsoperatoren.

<>

# All tests

function P { printf "[email protected]"; }
function EXPECT { printf "[email protected]"; }
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
2
stepse

Hier ist eine einfache Bash-Funktion, die keine externen Befehle verwendet. Es funktioniert für Versionsstrings, die bis zu drei numerische Teile enthalten - weniger als 3 ist auch in Ordnung. Es kann leicht für mehr erweitert werden. Es implementiert die Bedingungen =, <, <=, >, >= und !=.

#!/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>
2
codeforester

Für alte Version/busybox sort. Ein einfaches Formular liefert ungefähr Ergebnisse und funktioniert häufig.

sort -n

Dies ist besonders bei Versionen hilfreich, die Alpha-Symbole enthalten

10.c.3
10.a.4
2.b.5
2
Daniel YC Lin

Hier ist eine weitere reine Bash-Lösung ohne externe Aufrufe:

#!/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 noch einfacher ist es, wenn Sie sicher sind, dass die betreffenden Versionen nach dem ersten Punkt keine führenden Nullen 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 wird für 1.2.3 vs 1.3.1 vs 0.9.7 funktionieren, funktioniert aber nicht mit 1.2.3 vs 1.2.3.0 oder 1.01.1 vs 1.1.1

1
Vladimir Zorin

Hier ist eine Verfeinerung der Top-Antwort (Dennis's), die übersichtlicher 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
}
1
Kyle Rose

Ich habe noch eine andere Komparatorfunktion implementiert. Dieser hatte zwei spezifische Anforderungen: (i) Ich wollte nicht, dass die Funktion durch Verwendung von return 1 fehlschlug, sondern stattdessen echo; (ii) Da wir Versionen von einer git-Repository-Version abrufen, sollte "1.0" größer als "1.0.2" sein, was bedeutet, dass "1.0" vom 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.

1
Tiago L. Alves

Sie können version 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
0
Ivan Dyachenko

Hier ist eine weitere reine Bash-Version, die kleiner als die akzeptierte Antwort ist. Es wird nur geprüft, ob eine Version kleiner oder gleich einer "Mindestversion" ist, und es wird alphanumerische Sequenzen lexikographisch überprüft, was häufig zu einem falschen Ergebnis führt ("Momentaufnahme" ist nicht später als "release", um ein allgemeines Beispiel zu geben) . 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
}
0

Ich habe dieses Problem gefunden und gelöst, um eine zusätzliche (und kürzere und einfachere) Antwort hinzuzufügen ...

Der erste 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 sort -t '.' - g (oder sort -V, wie von kanaka erwähnt), um Versionen und einfachen Vergleich von bash-Strings zu sortieren, fand ich eine Lösung. 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 ist als die andere. Ich hoffe, dass dies immer noch jemandem helfen kann, der dies so einfach wie möglich macht.

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

0
JStrahl

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"
0
Maxxim

Ein anderer Ansatz (modifizierte Version von @joynes), bei dem gepunktete Versionen wie in der Frage gestellt verglichen werden
(d. h. 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

verwendungsbeispiel:

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 seit 1.10.1 ist größer als 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"

rückgabe: 0 seit 1.10.1 ist niedriger als 1.11

0
magiccrafter

Dies ist auch eine pure bash-Lösung, da printf eine integrierte 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//./ }
}
0
Christopher
### 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.

0
erh

Dank der Lösung von Dennis können wir die Vergleichsoperatoren '>', '<', '=', '==', '<=' und '> =' erweitern.

# 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 teste nur das wahr/falsch des Ergebnisses, wie:

if compver $ver1 '>' $ver2; then
    echo "Newer"
fi
0
Luke Lee

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"
0
Artieman