Sprawdź, czy wersja Bash to> = podany numer wersji

12

Muszę sprawdzić, czy numer wersji Bash to> = do określonego numeru. Na przykład mam:

$ 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.

Aby użyć tablic asocjacyjnych, numer wersji bash musi wynosić> = 4.

W moim skrypcie bash chciałbym przetestować jedną linijkę w możliwie najbardziej elegancki / wydajny / czytelny sposób, ale inne podejścia są również akceptowane.

WinEunuuchs2Unix
źródło
Czy spojrzałeś na zmienne $BASH_VERSIONi $BASH_VERSINFO?
steeldriver
@steeldriver Nie. To byłaby akceptowalna odpowiedź w konkretnym przypadku bash, jeśli chcesz ją opublikować. Jednak w innych przypadkach zmienne środowiskowe mogą być niedostępne.
WinEunuuchs2Unix
Nawiasem mówiąc, powiązane: askubuntu.com/questions/39309/…
Sergiy Kolodyazhnyy
@SergiyKolodyazhnyy, który jest powiązany tylko z bash. Chociaż szukam bashowego linku lub skryptu / funkcji, sprawdzenie numeru wersji bash nie jest ostatecznym celem. --versionPierwotną intencją była ogólna procedura sprawdzania wszystkich programów poprzez dołączanie i testowanie danych wyjściowych. Odpowiednio zredagowałem pytanie.
WinEunuuchs2Unix
Formaty numerów wersji różnią się znacznie w zależności od programu. Sposób, w jaki zgłaszają szczegóły wersji, również różni się bardzo. Po prostu nie jest praktyczne prosić o sposób sprawdzenia wersji losowego programu. To pytanie jest zbyt ogólne.
muru

Odpowiedzi:

16

Próbować:

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

BASH_VERSINFOjest zmienną tablicową tylko do odczytu, której członkowie przechowują informacje o wersji dla tego wystąpienia bash. Od czasu wprowadzenia bash 2.0 jest prawdopodobnie obsługiwany przez wszystkie napotkane wersje bash. Jednak, aby zachować ostrożność, dołączamy wartość domyślną 0dla każdego bash wcześniejszej wersji, dla którego ta zmienna nie jest ustawiona.

Wydobywanie informacji o wersji z innych programów

Pytałeś o LibreOffice, Python, jądro itp.

LibreOffice tworzy informacje o wersji, które wyglądają następująco:

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

Aby wyodrębnić numer wersji:

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

W przypadku python:

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

Aby uzyskać wersję jądra, użyj uname:

$ uname -r
4.9.0-2-amd64
John1024
źródło
Świetne odpowiedzi! Tylko uwaga, że ​​masz uname -r„4.9.0-2-amd64”, który mógłby przetestować większy niż mój „4.11.1-041101-ogólny” przy regularnym teście bash, gdy w rzeczywistości mój numer wersji jest większy.
WinEunuuchs2Unix
2
@ WinEunuuchs2Unix Narzędzia Python mogą dokładnie porównywać ciągi wersji. Zobacz Porównaj ciągi wersji w Pythonie
wjandrea,
John - przykładowy python można skrócić, o $ python --versionktóry zwraca Python 2.7.12. @ wjandrea-- dzięki za link +1. Być może mógłbym zbudować tabelę wszystkich nazw programów i minimalnych numerów wersji. Następnie przekaż tabelę do zmodyfikowanej kopii pythonpodanego linku. Ponieważ można wywoływać tylko skompilowany Python, można grubby pomyśleć, że istnieje plik binarny, aby to zrobić lub jest to możliwe w powłoce.
WinEunuuchs2Unix
9

Zamiast porównywać numery wersji, możesz bezpośrednio przetestować samą funkcję. declare -Azwraca 2(przynajmniej w Bash 3.2), jeśli nie rozpoznaje -A, więc przetestuj to (wyświetla również błąd):

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

( declare -A varkończy się również niepowodzeniem, jeśli varjest tablicą niepowiązaną, więc unsetnajpierw).

Chociaż tak naprawdę nie zakładam, że ktoś będzie chciał korzystać z funkcji backportu w Bash, ogólnie bardziej odpowiednie jest sprawdzanie funkcji, a nie wersji. Nawet w przypadku Basha ktoś może skompilować wersję z ograniczonymi funkcjami ...


Bardziej ogólny przypadek testowania numerów wersji składa się z dwóch części: 1) jak znaleźć poprawny numer wersji do przetestowania oraz 2) jak go porównać z inną wartością.

Pierwszy jest trudniejszy. Wiele programów podaje swój numer wersji za pomocą flagi wiersza polecenia, takiej jak --versionlub -v, ale format wyjściowy jest różny, a programowe wybranie numeru wersji może być trudne. Następnie pojawia się problem z zainstalowaniem kilku wersji tego samego programu w tym samym czasie.

Drugi zależy od pewnej znajomości formatu numerów wersji. dpkgmogę porównać numery wersji w stylu Debiana (które, jak sądzę, zawierają wersje typu semver jako podzbiór):

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

Lub po prostu połączyć powyższe:

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
źródło
5

Istnieje kilka sposobów na osiągnięcie tego, co chcesz osiągnąć.

1. Użyj $ BASH_VERSION

Wystarczy zobaczyć, co jest $BASH_VERSIONzmienne. Osobiście użyłbym podpowłoki tak:

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

Pamiętaj, że <<<składnia dla here-doc nie jest przenośna, jeśli zamierzasz jej używać /bin/sh, czyli Dash na Ubuntu i może być czymś innym w innym systemie

Alternatywnym sposobem jest użycie instrukcji case lub if. Osobiście zrobiłbym to:

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

Prawdopodobnie ze względu na przenośność powinieneś prawdopodobnie sprawdzić, czy taka zmienna jest w ogóle ustawiona w ogóle za pomocą czegoś takiego [ -n $BASH_VERSION ]

Można to całkowicie przepisać jako funkcję do użycia w skrypcie. Coś długiego w linii:

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

To nie jest jedna linijka, chociaż jest to znacznie lepsze. Jakość ponad ilość.

2. Sprawdź, co jest zainstalowane

W tym celu należy filtrować dane wyjściowe apt-cache policypodobne

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

dpkg-querymoże się również przydać przy filtrowaniu przez awk.

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

Zauważ, że nie jest to przenośne, ponieważ jeśli nie ma go dpkglub nie jest aptzainstalowane w systemie (na przykład RHEL lub FreeBSD), nic ci to nie da.

3. Użyj set -e, aby wyjść ze skryptu, jeśli wystąpi błąd

Jednym ze sposobów obejścia tego jest po prostu skorzystanie z tablic asocjacyjnych i wyjście, gdy bashnie można ich użyć. set -ewiersz poniżej #!/bin/bashpozwoli skryptowi zakończyć pracę, jeśli skrypt nie może użyć tablicy asocjacyjnej.

Będzie to wymagać od użytkownika wyraźnego powiedzenia: „Hej, naprawdę potrzebujesz wersji bash 4.3 lub nowszej, w przeciwnym razie skrypt nie będzie działał”. Odpowiedzialność spoczywa na użytkowniku, choć niektórzy mogą twierdzić, że nie jest to dobre podejście do tworzenia oprogramowania.

4. Porzuć wszelką nadzieję i napisz przenośne skrypty zgodne z POSIX

bashskrypty nie są przenośne, ponieważ ich składnia nie jest kompatybilna z powłoką Bourne'a. Jeśli skrypt, który piszesz, będzie używany w wielu różnych systemach, nie tylko w Ubuntu, porzuć wszelką nadzieję i znajdź sposoby na użycie czegoś innego niż tablice asocjacyjne. Może to obejmować dwie tablice lub parsowanie pliku konfiguracyjnego. Zastanów się także nad przejściem na inny język, Perl lub Python, gdzie składnia jest co najmniej bardziej przenośna niż bash.

Sergiy Kolodyazhnyy
źródło
Tablica asocjacyjna jest drugą z „dwóch tablic”, jak sugerujesz w sekcji 4. Jedynym celem jest utrzymanie klucza „ścieżka / do / nazwa-pliku”, a wartością jest indeks w tablicy głównej. Tablica asocjacyjna została utworzona w celu przyspieszenia wyszukiwania do regularnej tablicy indeksowanej, które są obecnie sekwencyjne. Chociaż i tak mam oko na Pythona, moja obecna kompatybilność bash koncentruje się na Ubuntu i być może Bash dla Windows 10. Podoba mi się twój skrypt, który można dostosować. Na przykład yad --versionzwraca, 0.37.0 (GTK+ 3.18.9)ale obecnie dostępne są nowe funkcje 0.39.
WinEunuuchs2Unix
świetna i pomocna odpowiedź. Dziękuję Ci!
qodeninja
2

Jednowarstwowy nie jest możliwy, ale możliwy jest skrypt bash

Opracowałem skrypt oparty na odpowiedziach w przepełnieniu stosu. Jedna z tych odpowiedzi doprowadziła do napisania przez pracownika Dell porównania numerów wersji w 2004 r. Dla aplikacji DKMS.

Kod

Poniższy skrypt bash musi zostać oznaczony jako wykonywalny za pomocą polecenia chmod a+x script-name. Używam nazwy /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
źródło