Questo è il secondo articolo di una serie incentrata sullo scripting di Gnu Bash. Nel primo articolo di bash scripting abbiamo appena creato lo script più semplice:comandi semplici, uno dopo l'altro. Abbiamo anche visto l'uso di alcune variabili. Questo articolo tratterà le strutture di controllo di bash.
Strutture di controllo
Come ho detto nell'articolo precedente, la pigrizia è la chiave del successo. Se devo eseguire dei comandi, poco dopo gli stessi comandi nello stesso ordine, quando arriva la terza volta apro un editor di testo e incollo quei comandi per salvarlo come script.
Dopo un paio di giorni dopo, lo script inizia a crescere, sia in estensione che in complessità. Ho bisogno di modi per controllare il flusso di esecuzione, ad esempio:
- se succede qualcosa fai qualcosa, o se qualcosa non succede fai un'altra cosa;
- mentre sta succedendo qualcosa, fai qualcosa ancora e ancora, o fai qualcosa ancora e ancora;
- o a seconda del valore di una variabile o di cosa è successo, fai questo o quello o un altro,
- ecc
Questo è ciò a cui sono destinate le strutture di controllo bash (in realtà, in bash o in qualsiasi altro linguaggio di programmazione). Ma prima, dobbiamo sapere su:
Stato di uscita
Dalla manpage di bash:
«Lo stato di uscita di un comando eseguito è il valore restituito dalla chiamata di sistema waitpid o da una funzione equivalente. Gli stati di uscita sono compresi tra 0 e 255. (...) Uno stato di uscita pari a zero indica successo (o, uguale a 'vero'). Uno stato di uscita diverso da zero indica un errore (o, è uguale a 'false').(…. Ad esempio:) Se un comando non viene trovato, il processo figlio creato per eseguirlo restituisce uno stato di 127. Se un comando viene trovato ma non è eseguibile, lo stato del reso è 126 ».
tl;dr:lo status di uscita di 0 significa vero , successo; qualsiasi altro valore significa falso . È importante imparare questo perché eseguire decisioni vero/falso significa controllare lo stato di uscita di un comando.
Bash memorizza lo stato di uscita dell'ultimo comando eseguito in $? variabile.

se-allora-altro
Questo è il se succede qualcosa (succede qualcosa significa un comando con uno stato di uscita pari a zero) fai qualcosa , oppure fai un'altra cosa. La sintassi è:
if command then command1 command2 ... else commandA commandB ... fi

Come puoi vedere nello screenshot, può essere scritto su una singola riga, separando ogni comando con un punto e virgola:
if command; then command1; command2;...; else commandA; commandB;...;fi
A seconda della complessità del blocco if-then potrebbe essere più facile da leggere o più difficile da leggere nello stile "oneliner".
Se vuoi eseguire un comando se succede qualcosa, esegui un altro comando solo se qualcos'altro succede? usa la parola chiave elif (else if):
if command then command1 command2 ... elif other_command commandA commandB ... else another block of commands fi
Un blocco if-then-else termina con la parola chiave fi (se indietro).
Confronto di stringhe e numeri
Ricordi le variabili? spesso succede qualcosa è che a seconda del valore di una variabile vogliamo eseguire alcuni comandi o altro.
Per confrontare se una stringa è uguale a qualche parola o un numero è inferiore ad altro utilizziamo il comando test . La sintassi generale è:
test expression
Se espressione viene omesso è considerato falso (stato di uscita> 0). Se espressione è solo una stringa di testo è vero. Questo potrebbe essere complicato, ad esempio:
$ test false; echo $? 0
Non stiamo testando lo stato di uscita del comando false (c'è un comando false ) ma la stringa false contro nulla. L'altro modo per eseguire il comando test è racchiudere l'espressione tra parentesi quadre. In questo altro modo è più facile leggere all'interno di un blocco if-then-else.
operandi | vero se |
espressione | vero |
! espressione | false (nega l'espressione) |
espressioneA -a espressioneB | E. entrambe le espressioni sono vere |
espressioneA -o espressioneB | Oppure. una delle espressioni è vera |
stringa1 =stringa2 | entrambe le stringhe sono uguali |
stringa1 !=stringa2 | le stringhe sono diverse |
int1 -eq int2 | intero1 è uguale a intero2 |
int1-ge int2 | int1>=int2 |
int1 -gt int2 | int1> int2 |
int1 -le int2 | int1 <=int2 |
int1 -lt int2 | int1 |
int1 -ne int2 | int1 non è uguale a int2 |
-e file | il file esiste |
-d directory | il file esiste ed è una directory |
Ci sono più espressioni, quelle sono quelle che ritengo più importanti. Leggi la manpage per il test per imparare le altre espressioni.

Fai attenzione che ci sia uno spazio tra parentesi quadre e l'espressione. Ora possiamo usare il test per fare qualcosa in base al valore di una variabile. Ad esempio:

caso
Quando vogliamo testare una variabile rispetto a diversi valori, possiamo aggiungere più elif al nostro blocco if-then-else in questo modo:
if [ $a = "value1" ] then command1 for value1 ... commandN for value1 elif [ $a = "value2" ] then command1 for value2 ... commandN for value2 ... elif [ $a = "valueN" ] then command1 for valueN ... commandN for valueN else command1 for every other value ... commandN for every other value fi
Oppure possiamo sostituire con un case comando più facile da leggere e da scrivere:
case expression in value1) command1 for value1 ... commandN for value1 ;; value2) command1 for value2 ... commandN for value2 ;; ... *) command1 for every other value ... commandN for every other value ;; esac
Inoltre, meno sequenze di tasti, non dimenticare di essere abbastanza pigro da far funzionare il computer per te. L'ultimo valore * è il caso predefinito, ogni altro valore non abbinato prima. Maiuscole al contrario, esac , è analogo all'affermazione he fi. Se il ;; viene utilizzato l'operatore, non vengono tentate corrispondenze successive dopo la prima corrispondenza del modello. Per testare il modello successivo usa ;;* operatore. Ad esempio:

mentre e fino a
Mentre è fare qualcosa ancora e ancora mentre qualcosa accade. E fino a quando è lo stesso ma negato (fai qualcosa ancora e ancora mentre qualcosa non sta accadendo). La sintassi generale è:
while command do command1 command2 ... commandN done
Per fare qualcosa mentre non sta accadendo (cioè lo stato esistente è diverso da 0), sostituisci while con fino a. Vedi alcuni esempi:

Notare che non hanno prodotto lo stesso output. Nel blocco while, quando $a raggiunge il valore 4, non è maggiore di 4, quindi il test viene valutato falso (e il codice di uscita è> 0). Sul blocco until, quando $a raggiunge il valore 4, non è maggiore di 4.
Un orologio nel tuo terminale
A volte sto testando un cronjob e questo mi rende ansioso fino a quando non viene eseguito. Uso il comando true (che, come puoi immaginare, produce uno stato di uscita 0) come condizione ed eseguo il comando date più e più volte:
while true do date sleep 1 clear done
Questo funzionerebbe più a lungo, ma posso interrompere con ctrl-c.
per-in -fare
Questo tipo di ciclo scorre su un elenco fornito assegnando ogni elemento a una variabile. Sintassi:
for i in list of words do command1 ... commandN done
Questo è più facile da vedere in un esempio:

Rimani sintonizzato
Le cose stanno diventando complesse, ma vogliamo che i nostri script prendano alcune decisioni per fare questo o quello. Dovevo trattare altri argomenti, ma questo articolo sulle strutture di controllo bash è cresciuto troppo.
Nei prossimi tratterò i tubi e il reindirizzamento. Probabilmente tornerò su queste strutture di controllo bash con gli esempi, qualcosa come usare l'output di un comando in un ciclo for-in e cose del genere.