Vérifiez si la version de Bash est> = numéro de version donné

11

Je dois tester si le numéro de version de Bash est> = à un numéro spécifique. Par exemple, j'ai:

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

Pour utiliser des tableaux associatifs, le numéro de version bash doit être> = 4.

Dans mon script bash, j'aimerais faire un test à une ligne de la manière la plus élégante / efficace / lisible possible, mais d'autres approches sont également acceptées.

WinEunuuchs2Unix
la source
Avez-vous regardé les variables $BASH_VERSIONet $BASH_VERSINFO?
steeldriver
@steeldriver Non. Ce serait une réponse acceptable dans le cas spécifique de bash si vous souhaitez le publier. Cependant, dans d'autres cas, les variables environnementales peuvent ne pas être disponibles.
WinEunuuchs2Unix
Par ailleurs, lié: askubuntu.com/questions/39309/…
Sergiy Kolodyazhnyy
@SergiyKolodyazhnyy qui est lié à bash uniquement. Bien que je recherche un bash one-liner ou un script / fonction vérifiant uniquement le numéro de version bash n'est pas le but ultime. Une routine générique pour vérifier tous les programmes en ajoutant --versionet en testant la sortie était l'intention initiale. J'ai modifié la question en conséquence.
WinEunuuchs2Unix
Les formats des numéros de version varient énormément d'un programme à l'autre. La façon dont ils rapportent les détails de la version varie également énormément. Il n'est tout simplement pas pratique de demander un moyen de vérifier la version d'un programme aléatoire. Cette question est trop large.
muru

Réponses:

15

Essayer:

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

BASH_VERSINFOest une variable de tableau en lecture seule dont les membres détiennent des informations de version pour cette instance de bash. Depuis qu'il a été introduit avec bash 2.0, il est probablement pris en charge par toutes les versions de bash que vous rencontrerez. Mais, pour être prudent, nous incluons une valeur par défaut de 0pour toute version antérieure bash pour laquelle cette variable n'est pas définie.

Extraire les informations de version à partir d'autres programmes

Vous avez posé des questions sur LibreOffice, Python, le noyau, etc.

LibreOffice produit des informations de version qui ressemblent à:

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

Pour extraire le numéro de version:

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

Pour python:

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

Pour obtenir la version du noyau, utilisez uname:

$ uname -r
4.9.0-2-amd64
John1024
la source
Excellentes réponses! Juste une note que votre uname -r"4.9.0-2-amd64" pourrait être supérieur au mien "4.11.1-041101-generic" avec un test bash normal alors qu'en réalité mon numéro de version est supérieur.
WinEunuuchs2Unix
2
@ WinEunuuchs2Unix Les outils Python peuvent comparer avec précision les chaînes de version. Voir Comparer les chaînes de version en Python
wjandrea
John-- l'exemple python peut être raccourci avec $ python --versionlequel retourne Python 2.7.12. @ wjandrea-- merci pour le lien +1. Je pourrais peut-être construire un tableau de tous les noms de programme appelés et les numéros de version minimaux. Passez ensuite le tableau à une copie modifiée du pythonlien que vous avez fourni. Parce que seul Python compilé peut être appelé par grubvous pensez qu'un binaire existe pour faire ceci ou son possible dans le shell.
WinEunuuchs2Unix
9

Au lieu de comparer les numéros de version, vous pouvez tester directement la fonctionnalité elle-même. declare -Arenvoie 2(au moins dans Bash 3.2) s'il ne reconnaît pas -A, alors testez cela (il affiche également une erreur):

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

( declare -A varéchoue également s'il vars'agit d'un tableau non associatif, unsetil le fait donc en premier.)

Bien que je ne suppose pas que quiconque va rétroporter des fonctionnalités dans Bash, en général, il est plus approprié de vérifier les fonctionnalités, pas les versions. Même dans le cas de Bash, quelqu'un pourrait compiler une version avec seulement des fonctionnalités limitées ...


Le cas plus général de test des numéros de version comporte deux parties: 1) comment trouver le numéro de version correct à tester, et 2) comment le comparer à une autre valeur.

Le premier est le plus difficile. De nombreux programmes indiquent leur numéro de version avec un indicateur de ligne de commande comme --versionou -v, mais le format de sortie varie et il peut être difficile de choisir le numéro de version par programme. Ensuite, il y a la possibilité d'avoir plusieurs versions du même programme installées en même temps.

La seconde dépend d'une certaine connaissance du format des numéros de version. dpkgpeut comparer les numéros de version de style Debian (qui, je pense, inclut des versions de type semver comme sous-ensemble):

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

Ou, juste pour combiner ce qui précède:

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
la source
5

Il y a deux façons d'aborder ce que vous voulez réaliser.

1. Utilisez $ BASH_VERSION

Il suffit juste de voir ce qui est $BASH_VERSIONvariable. Personnellement, j'utiliserais un sous-shell comme ceci:

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

Notez que la <<<syntaxe pour here-doc n'est pas portable, si vous allez l'utiliser avec /bin/sh, qui est Dash sur Ubuntu et pourrait être autre chose sur un système différent

Vous pouvez également utiliser la déclaration de cas ou la déclaration if. Personnellement, je ferais ceci:

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

Probablement pour des raisons de portabilité, vous devriez probablement vérifier si une telle variable est même définie en premier lieu avec quelque chose comme [ -n $BASH_VERSION ]

Cela peut totalement être réécrit en fonction à utiliser dans un script. Quelque chose de long les lignes de:

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

Ce n'est pas un monoplace, bien que ce soit beaucoup mieux. La qualité plutôt que la quantité.

2. Vérifiez ce qui est installé

Pour cela, vous devez filtrer la sortie de la apt-cache policysorte

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

dpkg-querypeut également être utile avec un filtrage via awk.

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

Notez que ce n'est pas portable, car s'il n'y en a pas dpkgou aptinstallé sur un système (par exemple, RHEL ou FreeBSD), cela ne vous fera aucun bien.

3. Utilisez set -e pour quitter le script en cas d'erreur

Une façon de le contourner consiste simplement à utiliser des tableaux associatifs et à quitter lorsque bashvous ne pouvez pas les utiliser. set -ela ligne ci #!/bin/bash- dessous permettra au script de se fermer si le script ne peut pas utiliser de tableau associatif.

Cela vous demandera de dire explicitement à l'utilisateur: "Hé, vous avez vraiment besoin de la version 4.3 ou supérieure de bash, sinon le script ne fonctionnera pas". Ensuite, la responsabilité incombe à l'utilisateur, même si certains pourraient affirmer que ce n'est pas vraiment une bonne approche pour le développement de logiciels.

4. Abandonnez tout espoir et écrivez des scripts portables, compatibles POSIX

bashles scripts ne sont pas portables car sa syntaxe n'est pas compatible avec le shell Bourne. Si le script que vous écrivez va être utilisé sur une gamme de systèmes différents, pas seulement Ubuntu seul, alors abandonnez tout espoir et trouvez des moyens d'utiliser autre chose que des tableaux associatifs. Cela peut inclure la présence de deux tableaux ou l'analyse d'un fichier de configuration. Pensez également à passer à un autre langage, Perl ou Python, où la syntaxe est au moins plus portable que bash.

Sergiy Kolodyazhnyy
la source
Le tableau associatif est le deuxième des "deux tableaux" comme vous le suggérez dans la section 4. Son seul but est de contenir la clé "path / to / file-name" et la valeur est l'index dans le tableau principal. Le tableau associatif a été créé pour accélérer les recherches dans le tableau indexé normal qui sont actuellement séquentielles. Bien que j'ai un œil sur Python de toute façon, ma compatibilité bash actuelle se concentre sur Ubuntu et peut-être Bash pour Windows 10. J'aime votre script qui peut être personnalisé. Par exemple, yad --versionretourne 0.37.0 (GTK+ 3.18.9)mais de nouvelles fonctionnalités sont actuellement disponibles 0.39.
WinEunuuchs2Unix
grande et utile réponse. Merci!
qodeninja
2

One-liner pas possible mais un script bash est possible

J'ai développé un script qui s'appuie sur les réponses dans Stack Overflow. L'une de ces réponses a conduit à une comparaison des numéros de version des employés Dell en 2004 pour l'application DKMS.

Le code

Le script bash ci-dessous doit être marqué comme exécutable à l'aide de la commande chmod a+x script-name. J'utilise le nom /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
la source