Con l’ultima uscita di questa rubrica abbiamo iniziato ad addentrarci nel meraviglioso mondo delle strutture di controllo, ed abbiamo visto i cicli while, for ed until.
Come ho già detto, le strutture di controllo sono la parte fondamentale di ogni linguaggio di programmazione, oltre ai cicli che abbiamo già visto, di queste strutture fanno parte anche le cosiddette istruzioni di selezione, che hanno un comportamento analogo a quello dei cicli, vale a dire di controllare l’esecuzione di blocchi di codice all’interno dello script.
Con i cicli fondamentalmente stabiliamo dei blocchi di codice da eseguire ripetutamente all’interno dello script, cosi anzichè riscrivere lo stesso pezzo di codice tutte le volte che ci serve, lo inseriamo in un ciclo e ne definiamo la condizioni di veridicità, fintanto che queste sono vere o meno il codice verrà eseguito…o meno
Con le istruzioni di selezione invece inseriamo all’interno dell’esecuzione una sorta di bivio, tramite queste istruzioni prendiamo una decisione su cosa fare basandoci ad esempio sul valore di una variabile.
IF
Con if eseguiamo un blocco di codice se la condizione che noi stabiliamo risulta essere vera, valgono le stesse regole che abbiamo visto per i cicli: la condizione di verifica va inserità all’interno di parentesi quadre oppure si può usare il comando test, ma noi già sappiamo cosa conviene usare vero?
Il ciclo if espresso nella sua forma più basilare fa uso della sintassi “if – then – fi” che tradotto significa “Se (if) la condizione è vera allora (then) esegui tutto questo blocco di codice e poi esci (fi) dal ciclo.”
La cosa che dobbiamo ricordare bene quando usiamo IF è che le parentesi quadre rappresentano un AND logico, questo significa che noi possiamo inserire più condizioni dentro il blocco tra quadre e dire ad if che, per eseguire il codice tra then ed fi, tutte le condizioni devono essere verificate.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
#!bin/bash read -p "Sei maggiorenne o minorenne?" VAR if [ "$VAR" != "maggiorenne" || "$VAR" != "minorenne" ]; then echo "Non hai risposto correttamente, ti ho chiesto se sei maggiorenne o minorenne, rilancia lo script" exit 1 fi if [ "$VAR" == "maggiorenne" ]; then echo "Ok allora puoi iscriverti ai corsi per la patente di guida B" exit 0 fi echo "Mi spiace, non sei idoneo per iscriverti a corsi per la patente di guida B" exit 0
Leggerne il funzionamento dovrebbe oramai essere abbastanza facile vero? Lo script pone come prima cosa una domanda ed attende una risposta dall’utente, ma non una risposta qualsiasi, bensi una risposta precisa che verrà valorizzata nella variabile $VAR: l’utente deve esattamente scrivere “maggiorenne” o “minorenne”, come facciamo per obbligarlo a fare ciò?
Diamo in pasto ad IF una doppia condizione di verifica, tra le parentesi quadre abbiamo detto “Se il valore di $VAR è diverso da maggiorenne e da minorenne allora esegui questo pezzo di codice”, questo è possibile farlo tramite l’inserimento di due pipe che in pratica – nella frase di poco fa – svolgono il ruolo di “e da”.
Dopo la prima verifica ne parte una seconda, nel caso la risposta fornita dall’utente è uguale a maggiorenne allora diamo il consenso a partecipare ai corsi per la patente di guida B, altrimenti – visto che non vi sono altre possibilità di risposta – significa che l’utente è minorenne, e quindi non può richiedere l’iscrizione ai corsi.
IF – ELSE
Lo stesso blocco di codice inerente il secondo IF si sarebbe potuto scrivere facendo uso del costrutto IF – ELSE, il significato è uguale però può capitare di doverne far uso in base a come deve essere strutturato lo script, poniamo che lo script non sia cosi sterile come nel caso qui sopra, ma sia solo una parte di uno script più grosso che pone una serie di domande per compilare – ad esempio – la richiesta di iscrizione ai corsi, come prima domanda noi verifichiamo che l’utente sia maggiorenne o minorenne, ed in base alla risposta valorizziamo una variabile $IDONEO che ci servirà per stampare il riepilogo finale da mandare alla segretaria dell’ufficio che si dovrà occupare di scremare tra le richieste valide e quelle non valide.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
#!/bin/bash ...Stesso pezzo di codice con la domanda ed il primo ciclo if... if [ "$VAR" == "maggiorenne" ]; then echo "Ok allora puoi iscriverti ai corsi per la patente di guida B" IDONEO=true else echo "Mi spiace, non puoi essere ammesso ai corsi per patente di guida B" IDONEO=false fi if [ "$IDONEO" == "true" ]; then echo "Domanda valida per l'accettazione" > riepilogo.txt else echo "Domanda NON valida per l'accettazione" > riepilogo.txt fi
Non cambia molto dalla prima sintassi come puoi vedere, in sostanza l’istruzione else in italiano significa altrimenti: “Se $CONDIZIONE è vera allora esegui $COMANDO1 altrimenti esegui $COMANDO2″.
IF – ELIF – ELSE
Dunque, come sai l’obiettivo è creare degli script fatti bene e funzionali, per fare questo – tenendo a mente gli esempi di cui sopra – tutto questo cucuzzaro di roba avremmo potuto scriverlo usando la forma più “complessa” di if, introducendo l’uso del costrutto if – elif – else.
Con questo costrutto possiamo toglierci di mezzo il primo ciclo if, quello che forza l’utente a scrivere “maggiorenne” o “minorenne”, ed eseguire tutto il codice con un solo costrutto if, un pò più grande, e che racchiude tutti i comandi dello script.
Anche qui il significato italiano cambia di poco, elif infatti in italiano si potrebbe tradurre con se altrimenti: ”Se $CONDIZIONE è uguale $VALORE1 allora esegui COMANDO1, se altrimenti è uguale a $VALORE2 allora esegui COMANDO2, altrimenti esegui COMANDO3″.
Basandoci su questa traduzione tutto lo script potremmo scriverlo cosi:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
#!/bin/bash read -p "Sei maggiorenne o minorenne?" RISPOSTA if [ "$RISPOSTA" == "maggiorenne" ]; then echo "Allora puoi iscriverti ai corsi" echo "Domanda valida per l'accettazione" > riepilogo.txt exit 0 elif [ "$RISPOSTA" == "minorenne" ]; then echo "Mi spiace, non hai l'età minima per l'iscrizione ai corsi" echo "Domanda NON valida per l'accettazione" > riepilogo.txt exit 0 else echo "Non hai inserito la risposta corretta, ti ho chiesto se sei maggiorenne o minorenne, rilancia lo script" exit 1 fi
Visto? alla fine usando la forma più adatta al nostro scopo abbiamo scritto un ciclo if un pò più grande ma che fa tutto il necessario, e non abbiamo costretto la shell a lanciare due o tre cicli if prima di concludere l’esecuzione, molto meglio cosi no?
CASE
Case è il mio ciclo preferito, non chiedermi il perchè, ognuno ha le proprie deviazioni :D e sinceramente non saprei nemmeno dirti il motivo, so solo che dove posso lo uso senza pensarci due volte. Case mi consente di scrivere script più “elastici”, di prevedere più possibilità con meno righe di codice, e vedrai tra poco cosa voglio dire.
Iniziamo col sapere che case, con un grosso sforzo di fantasia, in italiano significherebbe “nel caso che”. Noi con case sostanzialmente diciamo a bash “Nel caso che $VARIABILE abbia $VALORE allora esegui COMANDO1, in tutti gli altri casi esegui COMANDO2″, però dobbiamo tenere presente che $VALORE può anche rappresentare una serie di valori, quindi noi potremmo eseguire COMANDO1 se $VAR corrisponde ad una certa serie di valori, COMANDO2 per un altra certa serie di valori, e cosi via all’infinito per poi concludere con un generico “per tutti gli altri valori esegui COMANDO_X”.
Insomma è più facile a farlo che a dirlo, vediamolo un pò.
Prendiamo sempre il nostro esempio di prima, dopo un mese dall’utilizzo di questo script ci siamo resi conto che la gran parte delle domande non idonee in realtà erano dei falsi positivi, perchè la gente alla domanda “sei maggiorenne o minorenne” molte volte ha risposto “si” per dire “si, sono maggiorenne” (seriamente, quante volte lo facciamo nei discorsi di tutti i giorni? ), per evitare di costringere la nostra povera segretaria a spulciarsi ogni singola risposta di ogni singola domanda abbiamo deciso quindi di rendere lo script un pò più elastico e prevedere quindi più risposte.
Con il costrutto if – elif – else ci metteremmo una vita, dovremmo inserire condizioni di verifica multiple e lunghissime per ogni if – elif, e ne verrebbe fuori del codice mastodontico. Bash ci mette quindi a disposizione il costrutto case che ci consente di fare tutto questo con molte meno linee di codice:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
#!/bin/bash read -p "Sei maggiorenne o minorenne?" RISPOSTA case $RISPOSTA in maggiorenne|si|s|sono maggiorenne|y|sn mag*) echo "Allora puoi iscriverti ai corsi" echo "Domanda valida per l'accettazione" > riepilogo.txt exit 0 ;; minorenne|no|n|sono minorenne|n|sn min*) echo "Mi dispiace, non hai l'età minima per i corsi di patente B" echo "Domanda NON valida per l'accettazione" > riepilogo.txt exit 0 ;; *) echo "Non hai inserito una risposta corretta, ti ho chiesto se sei maggiorenne o minorenne, rilancia lo script" exit 1 ;; esac
Dai solo un occhiata a tutte le possibili risposte che ho incluso, e capirai perchè case è cosi tanto potente, avrai notato che diversamente da if per definire delle condizioni di verifica multiple qui abbiamo usato il singolo pipe, perchè non ci interessa che tutte le condizioni di verifica siano soddisfatte, ce ne basta solo una, e nel nostro esempio abbiamo – nel caso di risposta affermativa – previsto che l’utente possa rispondere “si” “s” “y” “sono maggiorenne” “sn mag*” (nel caso ci capiti un bimbominkia facebook dipendente), è interessante l’ultima condizione in particolare, perchè con l’asterisco io prevedo qualsiasi combinazione di caratteri dopo “mag”, l’utente potrebbe scrivere “sn maggiorenne” o “sn magg” o “sn maggr”, insomma qualsiasi risposta stile SMS l’abbiamo prevista.
Cose da sapere su case: l’apertura del costrutto prevede “case $VAR in” bisogna fare attenzione ad “in” altrimenti avremo un errore di sintassi, ogni condizione di verifica deve concludersi con una parentesi tonda ed il relativo blocco di comandi da eseguire deve chiudersi con un doppio “;” che si chiama terminatore, altrimenti la bash non sa quali sono le condizioni e non sa quali sono i comandi da eseguire.
L’ultima condizione, quella con “*)”, non è obbligatoria potremmo aver bisogno di un case che deve eseguire determinati comandi a seconda di determinate condizioni di verifica, all’infuori delle quali deve semplicemente uscire dal ciclo e continuare con l’esecuzione dello script.
Come per IF la chiusura del ciclo case si identifica scrivendo CASE al contrario, cioè “esac”.
Ed anche per oggi abbiamo concluso, come al solito ti invito a porre nei commenti domande o dubbi, se per caso hai già scritto un pò di codice e ti va di condividerlo fai pure, se c’è da migliorare qualcosa lo faremo insieme