Guida avanzata di scripting Bash: Un'approfondita esplorazione dell'arte dello scripting di shell | ||
---|---|---|
Indietro | Avanti |
Qualsiasi linguaggio di programmazione, che a ragione possa definirsi completo, deve consentire la verifica di una condizione e quindi comportarsi in base al suo risultato. Bash possiede il comando test, vari operatori parentesi quadre, parentesi rotonde e il costrutto if/then.
Il costrutto if/then verifica se l'exit status di un elenco di comandi è 0 (perché 0 significa "successo" per convenzione UNIX) e se questo è il caso, esegue uno o più comandi.
Esiste il comando specifico [ (parentesi quadra aperta). È sinonimo di test ed è stato progettato come builtin per ragioni di efficienza. Questo comando considera i suoi argomenti come espressioni di confronto, o di verifica di file, e restituisce un exit status corrispondente al risultato del confronto (0 per vero, 1 per falso).
Con la versione 2.02, Bash ha introdotto [[ ... ]], comando di verifica estesa, che esegue confronti in un modo più familiare ai programmatori in altri linguaggi. Va notato che [[ è una parola chiave, non un comando.
Bash vede [[ $a -lt $b ]] come un unico elemento che restituisce un exit status.
Anche i costrutti (( ... )) e let ... restituiscono exit status 0 se le espressioni aritmetiche valutate sono espanse ad un valore diverso da zero. Questi costrutti di espansione aritmetica possono, quindi, essere usati per effettuare confronti aritmetici.
let "1<2" restituisce 0 (poiché "1<2" espande a "1") (( 0 && 1 )) restituisce 1 (poiché "0 && 1" espande a "0") |
Un costrutto if può verificare qualsiasi comando, non solamente le condizioni comprese tra le parentesi quadre.
if cmp a b &> /dev/null # Sopprime l'output. then echo "I file a e b sono identici." else echo "I file a e b sono diversi." fi # L'utilissimo costrutto "if-grep": # -------------------------------- if grep -q Bash file then echo "File contiene almeno un'occorrenza di Bash." fi parola=Linux sequenza_lettere=inu if echo "$parola" | grep -q "$sequenza_lettere" # L'opzione "-q" di grep elimina l'output. then echo "$sequenza_lettere trovata in $parola" else echo "$sequenza_lettere non trovata in $parola" fi if COMANDO_CON_EXIT_STATUS_0_SE_NON_SI_VERIFICA_UN_ERRORE then echo "Comando eseguito." else echo "Comando fallito." fi |
Un costrutto if/then può contenere confronti e verifiche annidate.
if echo "Il prossimo *if* è parte del costrutto del primo *if*." if [[ $confronto = "intero" ]] then (( a < b )) else [[ $a < $b ]] fi then echo '$a è inferiore a $b' fi |
Dettagliata spiegazione della "condizione-if" cortesia di Stéphane Chazelas.
Esempio 7-1. Cos'è vero?
#!/bin/bash # Suggerimento: # se non siete sicuri di come certe condizioni verranno valutate, #+ controllatele con una verifica if. echo echo "Verifica \"0\"" if [ 0 ] # zero then echo "0 è vero." else echo "0 è falso." fi # 0 è vero. echo echo "Verifica \"1\"" if [ 1 ] # uno then echo "1 è vero." else echo "1 è falso." fi # 1 è vero. echo echo "Verifica \"-1\"" if [ -1 ] # meno uno then echo "-1 è vero." else echo "-1 è falso." fi # -1 è vero. echo echo "Verifica \"NULL\"" if [ ] # NULL (condizione vuota) then echo "NULL è vero." else echo "NULL è falso." fi # NULL è falso. echo echo "Verifica \"xyz\"" if [ xyz ] # stringa then echo "La stringa casuale è vero." else echo "La stringa casuale è falso." fi # La stringa casuale è vero. echo echo "Verifica \"\$xyz\"" if [ $xyz ] # Verifica se $xyz è nulla, ma... # è solo una variabile non inizializzata. then echo "La variabile non inizializzata è vero." else echo "La variabile non inizializzata è falso." fi # La variabile non inizializzata è falso. echo echo "Verifica \"-n \$xyz\"" if [ -n "$xyz" ] # Più corretto, ma pedante. then echo "La variabile non inizializzata è vero." else echo "La variabile non inizializzata è falso." fi # La variabile non inizializzata è falso. echo xyz= # Inizializzata, ma impostata a valore nullo. echo "Verifica \"-n \$xyz\"" if [ -n "$xyz" ] then echo "La variabile nulla è vero." else echo "La variabile nulla è falso." fi # La variabile nulla è falso. echo # Quando "falso" è vero? echo "Verifica \"falso\"" if [ "falso" ] # Sembra che "falso" sia solo una stringa. then echo "\"falso\" è vero." # e verifica se è vero. else echo "\"falso\" è falso." fi # "falso" è vero. echo echo "Verifica \"\$falso\"" # Ancora variabile non inizializzata. if [ "$falso" ] then echo "\"\$falso\" è vero." else echo "\"\$falso\" è falso." fi # "$falso" è falso. # Ora abbiamo ottenuto il risultato atteso. # Cosa sarebbe accaduto se avessimo verificato #+ la variabile non inizializzata "$vero"? echo exit 0 |
Esercizio. Si spieghi il comportamento del precedente Esempio 7-1.
if [ condizione-vera ] then comando 1 comando 2 ... else # Opzionale (può anche essere omesso). # Aggiunge un determinato blocco di codice che verrà eseguito se la #+ condizione di verifica è falsa. comando 3 comando 4 ... fi |
Quando if e then sono sulla stessa riga occorre mettere un punto e virgola dopo l'enunciato if per indicarne il termine. Sia if che then sono parole chiave. Le parole chiave (o i comandi) iniziano gli enunciati e prima che un nuovo enunciato possa incominciare, sulla stessa riga, è necessario che il precedente venga terminato.
|
elif è la contrazione di else if. Lo scopo è quello di annidare un costrutto if/then in un altro.
if [ condizione1 ] then comando1 comando2 comando3 elif [ condizione2 ] # Uguale a else if then comando4 comando5 else comando-predefinito fi |
Il costrutto if test condizione-vera è l'esatto equivalente di if [ condizione-vera ]. In quest'ultimo costrutto, la parentesi quadra sinistra , [, è un simbolo che invoca il comando test. La parentesi quadra destra di chiusura, ], non dovrebbe essere necessaria. Ciò nonostante, le più recenti versioni di Bash la richiedono.
Il comando test è un builtin Bash che verifica i tipi di file e confronta le stringhe. Di conseguenza, in uno script Bash, test non richiama l'eseguibile esterno /usr/bin/test, che fa parte del pacchetto sh-utils. In modo analogo, [ non chiama /usr/bin/[, che è un link a /usr/bin/test.
Se, per qualche ragione, desideraste usare /usr/bin/test in uno script Bash, allora specificatene il percorso completo. |
Esempio 7-2. Equivalenza di test, /usr/bin/test, [ ] e /usr/bin/[
#!/bin/bash echo if test -z "$1" then echo "Nessun argomento da riga di comando." else echo "Il primo argomento da riga di comando è $1." fi echo if /usr/bin/test -z "$1" # Stesso risultato del builtin "test". # ^^^^^^^^^^^^^ # Specificando il percorso completo del file. then echo "Nessun argomento da riga di comando." else echo "Il primo argomento da riga di comando è $1." fi echo if [ -z "$1" ] # Funzionalità identica al precedente blocco #+ di codice. # if [ -z "$1" dovrebbe funzionare, ma... #+ Bash risponde con il messaggio d'errore di missing close-bracket. then echo "Nessun argomento da riga di comando." else echo "Il primo argomento da riga di comando è $1." fi echo if /usr/bin/[ -z "$1" ] # Ancora, funzionalità identica alla precedente. # if /usr/bin/[ -z "$1" # Funziona, ma dà un messaggio d'errore. # # Nota: # Il problema è stato risolto # + nella versione Bash 3.x then echo "Nessun argomento da riga di comando." else echo "Il primo argomento da riga di comando è $1." fi echo exit 0 |
Il costrutto [[ ]] è la versione Bash più versatile di [ ]. È il comando di verifica esteso, adottato da ksh88.
Non può aver luogo alcuna espansione di nome di file o divisione di parole tra [[ e ]], mentre sono consentite l'espansione di parametro e la sostituzione di comando. |
file=/etc/passwd if [[ -e $file ]] then echo "Il file password esiste." fi |
L'utilizzo del costrutto di verifica [[ ... ]] al posto di [ ... ] può evitare molti errori logici negli script. Per esempio, gli operatori &&, ||, < e > funzionano correttamente in una verifica [[ ]], mentre potrebbero dare degli errori con il costrutto [ ] . |
Dopo un if non sono strettamente necessari né il comando test né i costrutti parentesi quadre ( [ ] o [[ ]] ).
Per questo motivo, una condizione tra parentesi quadre può essere utilizzata da sola, senza if, se abbinata ad un costrutto lista.
|
Il costrutto (( )) espande e valuta un'espressione aritmetica. Se il risultato della valutazione dell'espressione è zero, viene restituito come exit status 1, ovvero "falso". Una valutazione diversa da zero restituisce come exit status 0, ovvero "vero". Questo è in contrasto marcato con l'utilizzo di test e dei costrutti [ ] precedentemente discussi.
Esempio 7-3. Verifiche aritmetiche utilizzando (( ))
#!/bin/bash # Verifiche aritmetiche. # Il costrutto (( ... )) valuta e verifica le espressioni aritmetiche. # Exit status opposto a quello fornito dal costrutto [ ... ]! (( 0 )) echo "l'exit status di \"(( 0 ))\" è $?." # 1 (( 1 )) echo "L'exit status di \"(( 1 ))\" è $?." # 0 (( 5 > 4 )) # vero echo "L'exit status di \"(( 5 > 4 ))\" è $?." # 0 (( 5 > 9 )) # falso echo "L'exit status di \"(( 5 > 9 ))\" è $?." # 1 (( 5 - 5 )) # 0 echo "L'exit status di \"(( 5 - 5 ))\" è $?." # 1 (( 5 / 4 )) # Divisione o.k. echo "L'exit status di \"(( 5 / 4 ))\" è $?." # 0 (( 1 / 2 )) # Risultato della divisione <1. echo "L'exit status di \"(( 1 / 2 ))\" è $?." # Arrotondato a 0. # 1 (( 1 / 0 )) 2>/dev/null # Divisione per 0 non consentita. # ^^^^^^^^^^^ echo "L'exit status di \"(( 1 / 0 ))\" è $?." # 1 # Che funzione ha "2>/dev/null"? # Cosa succederebbe se fosse tolto? # Toglietelo, quindi rieseguite lo script. exit 0 |