Controlla se la versione di Bash è> = numero di versione indicato


12

Devo verificare se il numero di versione di Bash è> = per un numero specifico. Ad esempio ho:

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

Per utilizzare le matrici associative il numero della versione bash deve essere> = 4.

Nel mio script bash mi piacerebbe fare un test one-liner nel modo più elegante / efficiente / leggibile possibile, ma sono accettati anche altri approcci.


Hai esaminato le variabili $BASH_VERSIONe $BASH_VERSINFO?
steeldriver,

@steeldriver No. Sarebbe una risposta accettabile nel caso specifico bash se desideri pubblicarlo. Tuttavia, per altri casi, le variabili ambientali potrebbero non essere disponibili.
WinEunuuchs2Unix


@SergiyKolodyazhnyy che è legato solo a bash. Anche se sto cercando un bash one-liner o script / funzione che controlla solo il numero di versione bash non è l'obiettivo finale. L' --versionintenzione originale era una routine generica per controllare tutti i programmi aggiungendo e testando l'output. Ho modificato la domanda di conseguenza.
WinEunuuchs2Unix

I formati dei numeri di versione variano notevolmente tra i programmi. Anche il modo in cui riportano i dettagli della versione varia notevolmente. Semplicemente non è pratico chiedere un modo per controllare la versione di un programma casuale. Questa domanda è troppo ampia.
muru,

Risposte:


16

Provare:

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

BASH_VERSINFOè una variabile di array di sola lettura i cui membri contengono informazioni sulla versione per questa istanza di bash. Da quando è stato introdotto con bash 2.0, è probabilmente supportato da tutte le versioni di bash che incontrerai. Ma, per essere prudenti, includiamo un valore predefinito di 0per qualsiasi versione precedente bash per cui questa variabile non è impostata.

Estrazione delle informazioni sulla versione da altri programmi

Hai chiesto informazioni su LibreOffice, Python, kernel, ecc.

LibreOffice produce informazioni sulla versione che assomigliano a:

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

Per estrarre il numero di versione:

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

Per Python:

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

Per ottenere la versione del kernel, utilizzare uname:

$ uname -r
4.9.0-2-amd64

Grandi risposte! Solo una nota che il tuo uname -rè "4.9.0-2-amd64" che potrebbe testare più del mio "4.11.1-041101-generico" con un normale test bash quando in realtà il mio numero di versione è maggiore.
WinEunuuchs2Unix

2
@ Gli strumenti Python di WinEunuuchs2Unix possono confrontare accuratamente le stringhe di versione. Vedi Confronta le stringhe di versione in Python
wjandrea,

John: l'esempio di Python può essere abbreviato con il $ python --versionquale ritorna Python 2.7.12. @ wjandrea-- grazie per il link +1. Forse potrei costruire una tabella con tutti i nomi di programmi chiamati e numeri di versione minimi. Quindi passare la tabella a una copia modificata del pythoncollegamento fornito. Poiché solo Python compilato può essere chiamato da grubte, potresti pensare che esiste un binario per fare questo o il suo possibile nella shell.
WinEunuuchs2Unix,

9

Invece di confrontare i numeri di versione, è possibile verificare direttamente la funzionalità stessa. declare -Aritorna 2(almeno in Bash 3.2) se non riconosce -A, quindi prova per questo (stampa anche un errore):

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

( declare -A varfallisce anche se varè un array non associativo, quindi unsetprima.)

Anche se non presumo davvero che qualcuno eseguirà il backport delle funzionalità in Bash, in generale è più appropriato verificare le funzionalità, non le versioni. Anche nel caso di Bash, qualcuno potrebbe compilare una versione con funzionalità limitate ...


Il caso più generale di test dei numeri di versione ha due parti: 1) come trovare il numero di versione corretto da testare e 2) come confrontarlo con un altro valore.

Il primo è il più difficile. Molti programmi indicano il loro numero di versione con un flag della riga di comando come --versiono -v, ma il formato di output varia e la selezione a livello di codice del numero di versione può essere difficile. Quindi c'è il problema di possedere diverse versioni dello stesso programma installate contemporaneamente.

Il secondo dipende da una certa conoscenza del formato dei numeri di versione. dpkgposso confrontare i numeri di versione in stile Debian (che penso includa le versioni di tipo semver come sottoinsieme):

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

Oppure, solo per combinare quanto sopra:

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

5

Ci sono un paio di modi per affrontare ciò che vuoi ottenere.

1. Usa $ BASH_VERSION

È sufficiente vedere solo cosa c'è nella $BASH_VERSIONvariabile. Personalmente userei subshell in questo modo:

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

Nota che la <<<sintassi per here-doc non è portatile, se hai intenzione di usarla /bin/sh, che è Dash su Ubuntu e potrebbe essere qualcos'altro su un sistema diverso

Il modo alternativo è tramite la dichiarazione case o if. Personalmente, farei questo:

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

Probabilmente per motivi di portabilità, probabilmente dovresti controllare se tale variabile è addirittura impostata in primo luogo con qualcosa di simile [ -n $BASH_VERSION ]

Questo può essere completamente riscritto come funzione da utilizzare in uno script. Qualcosa di molto lungo le linee di:

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

Questo non è un liner, sebbene sia molto meglio. Qualità più che quantità.

2. Controlla cosa è installato

Per questo è necessario filtrare l'output in questo apt-cache policymodo

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

dpkg-querypuò anche tornare utile con alcuni filtri tramite awk.

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

Nota che questo non è portatile, poiché se non è presente dpkgo aptinstallato su un sistema (ad esempio, RHEL o FreeBSD), non ti farà nulla di buono.

3. Utilizzare set -e per uscire dallo script in caso di errore

Un modo per aggirarlo è semplicemente andare avanti usando array associativi e uscire quando bashnon è possibile utilizzarli. set -ela riga seguente #!/bin/bashconsentirà di chiudere lo script se lo script non può utilizzare l'array associativo.

Ciò richiederà di dire esplicitamente all'utente: "Ehi, hai davvero bisogno di bash versione 4.3 o successiva, altrimenti lo script non funzionerà". Quindi la responsabilità spetta all'utente, anche se alcuni potrebbero sostenere che questo non è davvero un buon approccio allo sviluppo del software.

4. Abbandonare ogni speranza e scrivere script portatili, conformi a POSIX

bashgli script non sono portatili perché la sua sintassi non è compatibile con la shell Bourne. Se lo script che stai scrivendo verrà utilizzato su una gamma di sistemi diversi, non solo Ubuntu da solo, allora abbandona ogni speranza e trova modi per utilizzare qualcosa di diverso dagli array associativi. Ciò potrebbe includere la presenza di due array o l'analisi di un file di configurazione. Considera anche il passaggio a una lingua diversa, Perl o Python, in cui la sintassi è almeno più portabile di bash.


L'array associativo è il secondo di "due array" come suggerito nella sezione 4. Il suo unico scopo è quello di contenere la chiave "percorso / a / nome-file" e il valore è l'indice all'interno dell'array principale. L'array associativo è stato creato per velocizzare le ricerche nell'array indicizzato regolare che sono attualmente sequenziali. Anche se ho sempre tenuto d'occhio Python, la mia attuale compatibilità bash si concentra su Ubuntu e forse su Bash per Windows 10. Mi piace il tuo script che può essere personalizzato. Ad esempio, yad --versionritorna 0.37.0 (GTK+ 3.18.9)ma al momento sono presenti nuove funzionalità 0.39.
WinEunuuchs2Unix

ottima e utile risposta. grazie!
qodeninja,

2

One-liner non è possibile ma è possibile uno script bash

Ho sviluppato uno script che attinge alle risposte in Stack Overflow. Una di queste risposte ha portato un dipendente Dell a scrivere confronti di numeri di versione nel 2004 per l'applicazione DKMS.

Il codice

Lo script bash di seguito deve essere contrassegnato come eseguibile utilizzando il comando chmod a+x script-name. Sto usando il nome /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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.