Überprüfen Sie, ob die Bash-Version> = die angegebene Versionsnummer ist

11

Ich muss testen, ob die Bash-Versionsnummer> = für eine bestimmte Nummer ist. Zum Beispiel habe ich:

$ bash --version
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Um assoziative Arrays verwenden zu können, muss die Bash-Versionsnummer> = 4 sein.

In meinem Bash-Skript möchte ich einen Einzeilentest auf die eleganteste / effizienteste / lesbarste Art und Weise durchführen, aber auch andere Ansätze werden akzeptiert.

WinEunuuchs2Unix
quelle
Haben Sie sich die Variablen $BASH_VERSIONund angesehen $BASH_VERSINFO?
Steeldriver
@steeldriver Nein. Das wäre eine akzeptable Antwort in einem bestimmten Fall, wenn Sie sie veröffentlichen möchten. In anderen Fällen sind die Umgebungsvariablen jedoch möglicherweise nicht verfügbar.
WinEunuuchs2Unix
Übrigens verwandt: askubuntu.com/questions/39309/…
Sergiy Kolodyazhnyy
@SergiyKolodyazhnyy, die nur mit Bash zusammenhängt. Obwohl ich nach einem Bash-Einzeiler oder einem Skript / einer Funktion suche, ist es nicht das ultimative Ziel, nur die Bash-Versionsnummer zu überprüfen. Eine allgemeine Routine zum Überprüfen aller Programme durch Anhängen --versionund Testen der Ausgabe war die ursprüngliche Absicht. Ich habe die Frage entsprechend bearbeitet.
WinEunuuchs2Unix
Die Versionsnummernformate variieren stark zwischen den Programmen. Die Art und Weise, wie Versionsdetails gemeldet werden, ist ebenfalls sehr unterschiedlich. Es ist einfach nicht praktisch, nach einer Möglichkeit zu fragen, die Version eines zufälligen Programms zu überprüfen. Diese Frage ist zu weit gefasst.
Muru

Antworten:

14

Versuchen:

$ [ "${BASH_VERSINFO:-0}" -ge 4 ] && echo "bash supports associative arrays"
bash supports associative arrays

BASH_VERSINFOist eine schreibgeschützte Array-Variable, deren Mitglieder Versionsinformationen für diese Bash-Instanz enthalten. Da es mit Bash 2.0 eingeführt wurde, wird es wahrscheinlich von allen Bash-Versionen unterstützt, auf die Sie stoßen werden. Um jedoch vorsichtig zu sein, geben wir 0für alle Bash-Versionen früherer Versionen, für die diese Variable nicht festgelegt ist, den Standardwert von an .

Extrahieren von Versionsinformationen aus anderen Programmen

Sie haben nach LibreOffice, Python, Kernel usw. gefragt.

LibreOffice erzeugt Versionsinformationen, die wie folgt aussehen:

$ libreoffice --version
LibreOffice 5.2.6.2 20m0(Build:2)

So extrahieren Sie die Versionsnummer:

$ libreoffice --version | cut -d' ' -f2
5.2.6.2

Für Python:

$ python -c 'import platform; print(platform.python_version())'
2.7.13

Verwenden Sie zum Abrufen der Kernelversion uname:

$ uname -r
4.9.0-2-amd64
John1024
quelle
Tolle Antworten! Nur ein Hinweis, dass Ihr uname -r"4.9.0-2-amd64" ist, der mit einem regulären Bash-Test größer als meiner "4.11.1-041101-generic" testen könnte, wenn in Wirklichkeit meine Versionsnummer größer ist.
WinEunuuchs2Unix
2
@ WinEunuuchs2Unix Python-Tools können Versionszeichenfolgen genau vergleichen. Siehe Versionszeichenfolgen in Python vergleichen
wjandrea
John - das Python-Beispiel kann verkürzt werden, mit $ python --versiondem zurückgegeben wird Python 2.7.12. @ wjandrea-- danke für den Link +1. Vielleicht könnte ich eine Tabelle mit allen aufgerufenen Programmnamen und minimalen Versionsnummern erstellen. Übergeben Sie die Tabelle dann an eine geänderte Kopie des von pythonIhnen angegebenen Links. Da nur kompiliertes Python von grubIhnen aufgerufen werden kann, würde man denken, dass eine Binärdatei existiert, um dies oder das in der Shell mögliche zu tun.
WinEunuuchs2Unix
9

Anstatt Versionsnummern zu vergleichen, können Sie die Funktion selbst direkt testen. declare -AGibt zurück 2(zumindest in Bash 3.2), wenn es nicht erkannt -Awird. Testen Sie dies (es wird auch ein Fehler ausgegeben):

unset assoc
if ! declare -A assoc ; then
    echo "associative arrays not supported!"
    exit 1
fi

( declare -A varschlägt auch fehl, wenn vares sich um ein nicht assoziatives Array handelt, also unsetzuerst.)

Ich gehe zwar nicht wirklich davon aus, dass jemand Funktionen in Bash zurückportieren wird, aber im Allgemeinen ist es besser, nach den Funktionen zu suchen, nicht nach den Versionen. Selbst in Bashs Fall könnte jemand eine Version mit nur eingeschränkten Funktionen kompilieren ...


Der allgemeinere Fall des Testens von Versionsnummern besteht aus zwei Teilen: 1) wie man die richtige zu testende Versionsnummer findet und 2) wie man sie mit einem anderen Wert vergleicht.

Der erste ist der schwierigere. Viele Programme teilen ihre Versionsnummer mit einem Befehlszeilenflag wie --versionoder mit -v, aber das Ausgabeformat variiert und die programmgesteuerte Auswahl der Versionsnummer kann schwierig sein. Dann gibt es das Problem, dass möglicherweise mehrere Versionen desselben Programms gleichzeitig installiert sind.

Die zweite hängt von einigen Kenntnissen über das Format der Versionsnummern ab. dpkgkann Versionsnummern im Debian-Stil vergleichen (die meiner Meinung nach Versionen vom Typ Semver als Teilmenge enthalten):

if dpkg --compare-versions 4.3.30 ge 4.0.0 ; then
    echo "it's version 4.x"
fi

Oder um nur das Obige zu kombinieren:

bashver=$( bash --version | sed -Ee 's/GNU bash, version ([0-9.]+).*/\1/;q' )
if dpkg --compare-versions "$bashver" ge 4.0.0 ; then
    echo "'bash' in your path is version 4.x"
fi
ilkkachu
quelle
5

Es gibt verschiedene Möglichkeiten, sich dem zu nähern, was Sie erreichen möchten.

1. Verwenden Sie $ BASH_VERSION

Es reicht aus, nur zu sehen, was in der $BASH_VERSIONVariablen enthalten ist. Persönlich würde ich Subshell wie folgt verwenden:

$ (read -d "." version trash <<< $BASH_VERSION; echo "$version" )
4

Beachten Sie, dass die <<<Syntax für here-doc nicht portierbar ist, wenn Sie sie verwenden möchten. Dies /bin/shist Dash unter Ubuntu und möglicherweise etwas anderes auf einem anderen System

Der alternative Weg ist über die case-Anweisung oder die if-Anweisung. Persönlich würde ich das tun:

bash-4.3$ case $BASH_VERSION in 4.*) echo "Can use associative arrays";; ?) echo "can't use associative arrays" ;; esac
Can use associative arrays

Wahrscheinlich sollten Sie aus Gründen der Portabilität prüfen, ob eine solche Variable überhaupt erst mit so etwas gesetzt wurde [ -n $BASH_VERSION ]

Dies kann vollständig als Funktion umgeschrieben werden, die in einem Skript verwendet werden soll. Etwas langes die Zeilen von:

#!/bin/bash
version_above_4(){
    # check if $BASH_VERSION is set at all
    [ -z $BASH_VERSION ] && return 1

    # If it's set, check the version
    case $BASH_VERSION in 
        4.*) return 0 ;;
        ?) return 1;; 
    esac
}

if version_above_4
then
    echo "Good"
else
    echo "No good"
fi

Dies ist kein Einzeiler, obwohl dies viel besser ist. Qualität vor Quantität.

2. Überprüfen Sie, was installiert ist

Dafür müssen Sie die Ausgabe von apt-cache policyso filtern

$ apt-cache policy bash | awk -F '[:.]' '/Installed:/{printf "%s\n",substr($2,2)}'
4

dpkg-querykann auch mit etwas Filterung über nützlich sein awk.

$ dpkg-query -W bash | awk '{print substr($2,1,1)}'   
4

Beachten Sie, dass dies nicht portabel ist, da es Ihnen nichts nützt, wenn auf einem System (z. B. RHEL oder FreeBSD) kein System installiert dpkgoder aptinstalliert ist.

3. Beenden Sie das Skript mit set -e, wenn ein Fehler auftritt

Eine Möglichkeit, dies zu umgehen, besteht darin, einfach assoziative Arrays zu verwenden und zu beenden, wenn bashsie nicht verwendet werden können. set -eIn der folgenden Zeile #!/bin/bashkann das Skript beendet werden, wenn das Skript kein assoziatives Array verwenden kann.

Dazu müssen Sie dem Benutzer explizit mitteilen: "Hey, Sie benötigen wirklich Bash-Version 4.3 oder höher, sonst funktioniert das Skript nicht." Dann liegt die Verantwortung beim Benutzer, obwohl einige argumentieren könnten, dass dies kein wirklich guter Ansatz für die Softwareentwicklung ist.

4. Geben Sie alle Hoffnung auf und schreiben Sie tragbare, POSIX-kompatible Skripte

bashSkripte sind nicht portierbar, da ihre Syntax nicht mit der Bourne-Shell kompatibel ist. Wenn das Skript, das Sie schreiben, auf einer Reihe verschiedener Systeme verwendet werden soll, nicht nur auf Ubuntu allein, dann geben Sie alle Hoffnung auf und finden Sie Möglichkeiten, etwas anderes als assoziative Arrays zu verwenden. Dies kann das Vorhandensein von zwei Arrays oder das Parsen einer Konfigurationsdatei umfassen. Wechseln Sie auch zu einer anderen Sprache, Perl oder Python, bei der die Syntax zumindest portabler ist als bash.

Sergiy Kolodyazhnyy
quelle
Das assoziative Array ist das zweite von "zwei Arrays", wie Sie in Abschnitt 4 vorschlagen. Es dient ausschließlich dazu, den Schlüssel "Pfad / zu / Dateiname" zu speichern, und der Wert ist der Index innerhalb des Hauptarrays. Das assoziative Array wurde erstellt, um die Suche in das reguläre indizierte Array zu beschleunigen, das derzeit sequentiell ist. Obwohl ich Python sowieso im Auge habe, konzentriert sich meine aktuelle Bash-Kompatibilität auf Ubuntu und vielleicht Bash für Windows 10. Ich mag Ihr Skript, das angepasst werden kann. Zum Beispiel yad --versionkehrt zurück, 0.37.0 (GTK+ 3.18.9)aber neue Funktionen sind derzeit in 0.39.
WinEunuuchs2Unix
tolle und hilfreiche Antwort. Danke!
Qodeninja
2

Einzeiler nicht möglich, aber ein Bash-Skript ist möglich

Ich habe ein Skript entwickelt, das auf Antworten in Stack Overflow basiert. Eine dieser Antworten führte dazu, dass ein Dell-Mitarbeiter 2004 Versionsnummernvergleiche für die DKMS-Anwendung schrieb.

Der Code

Das folgende Bash-Skript muss mit dem Befehl als ausführbar markiert werden chmod a+x script-name. Ich benutze den Namen /usr/local/bin/testver:

#!/bin/bash

# NAME: testver
# PATH: /usr/local/bin
# DESC: Test a program's version number >= to passed version number
# DATE: May 21, 2017. Modified August 5, 2019.

# CALL: testver Program Version

# PARM: 1. Program - validated to be a command
#       2. Version - validated to be numberic

# NOTE: Extracting version number perl one-liner found here:
#       http://stackoverflow.com/questions/16817646/extract-version-number-from-a-string

#       Comparing two version numbers code found here:
#       http://stackoverflow.com/questions/4023830/how-compare-two-strings-in-dot-separated-version-format-in-bash

# Map parameters to coder-friendly names.
Program="$1"
Version="$2"

# Program name must be a valid command.
command -v $Program >/dev/null 2>&1 || { echo "Command: $Program not found. Check spelling."; exit 99; }

# Passed version number must be valid format.
if ! [[ $Version =~ ^([0-9]+\.?)+$ ]]; then
    echo "Version number: $Version has invalid format. Aborting.";
    exit 99
fi

InstalledVersion=$( "$Program" --version | perl -pe '($_)=/([0-9]+([.][0-9]+)+)/' )
# echo "Installed Version: $InstalledVersion"

if [[ $InstalledVersion =~ ^([0-9]+\.?)+$ ]]; then
    l=(${InstalledVersion//./ })
    r=(${Version//./ })
    s=${#l[@]}
    [[ ${#r[@]} -gt ${#l[@]} ]] && s=${#r[@]}

    for i in $(seq 0 $((s - 1))); do
        # echo "Installed ${l[$i]} -gt Test ${r[$i]}?"
        [[ ${l[$i]} -gt ${r[$i]} ]] && exit 0 # Installed version > test version.
        [[ ${l[$i]} -lt ${r[$i]} ]] && exit 1 # Installed version < test version.
    done

    exit 0 # Installed version = test version.
else
    echo "Invalid version number found for command: $Program"
    exit 99
fi

echo "testver - Unreachable code has been reached!"
exit 255
WinEunuuchs2Unix
quelle