Shell Bash - 4 - Espressioni condizionali

Posted by RedBlue on November 18, 2011 · 7 mins read

Oggi affronteremo un altro argomento fondamentale nello scripting con la Shell Bash: le espressioni condizionali, ovvero i costrutti if - if/then/elif/else - case.

Cominciamo col chiarire a cosa servono questi costrutti: può accadere in uno script di avere la necessità di operare in modi diversi a seconda del risultato di una certa operazione e/o di un comando. La sintassi più elementare è la seguente:

if (CONDIZIONE); then

    (ISTRUZIONI);

fi
èèè

Che letteralmente significa "se si verifica CONDIZIONE allora esegui ISTRUZIONI". La condizione di controllo può essere un confronto tra numeri o stringhe, ma anche semplicemente l'esito di un comando (ovvero, se il comando va a buon fine = restituisce valore zero, allora esegui le istruzioni successive).
Due importanti precisazioni: 1) ad ogni if deve corrispondere un fi, che sta ad indicare all'interprete che il costrutto è terminato, e 2) ogni condizione restituisce sempre un valore numerico, pari a zero se il risultato è vero (true), altrimenti diverso da zero (che viene ovviamente interpretato come falso). Questo significa che anche un intero comando può essere impiegato in un costrutto condizionale, ovviamente perchè se tale comando va a buon fine verrà restituito zero e sarà interpretato come true, altrimenti come false.

Vediamo un piccolissimo script di esempio:

#!/bin/bash

clear

echo -e "Inizio script\n";

if [[ -e /etc/passwd ]]; then

        echo "File /etc/passwd esistente";

fi

if grep -q root /etc/passwd; then

        echo "Utente root verificato";

fi

echo -e "\nFine script";

Questo script non fa altro che verificare l'esistenza del file /etc/passwd (riga 4), per poi verificare l'esistenza dell'utente root (riga 7). Completano lo script il comando clear che non fa altro che pulire la shell, e due semplici comandi echo, che scrivono a video la stringa indicata (l'opzione -e invece abilita le sequenze di escape, il \n nell'esempio, che viene interpretato come "vai a capo").

Soffermiamoci sulla condizione nell'istruzione alla riga 4:

  [[ -e /etc/passwd ]];

Il -e indica all'interprete di verificare l'esistenza del file indicato, di seguito un elenco di tali espressioni:

è èè èè
Operatore Descrizione
-a, -e Restituisce true se il file esiste
-s Restituisce true se il file esiste ed ha dimensione maggiore di zero byte
-d, -f, -h Restituisce true se il file esiste ed è una directory, o un file regolare, o un link
-r, -w, -x Restituisce true se lo script ha i privilegi di lettura, o di scrittura, o di esecuzione sul file
-nt, -ot Restituisce true se il primo file è più recente del secondo (nt) o se il secondo è più recente del primo (ot)
-eq, -ne Restituisce true se tra due stringhe queste hanno lo stesso valore intero o due valori interi differenti
-lt, -le Restituisce true se la prima stringa ha un valore minore o minore o uguale al valore della seconda
-gt, -ge Restituisce true se la prima stringa ha un valore maggiore o maggiore o uguale al valore della seconda

E' importante anche chiarire il modo corretto di utilizzo della sintassi [[ ... ]]: attenzione!! Tra le doppie parentesi (sia di apertura che di chiusura) e l'espressione al loro interno è obbligatorio inserire gli spazi, altrimenti non verrà interpretato correttamente. Un'ultima cosa, si può anche trovare ancora la stessa espressione indicata con [ ... ], ovvero con una sola parentesi quadra, è una scrittura vecchiotta, meglio usare quella che ho indicato prima.

Lo script precedente può essere migliorato: supponiamo di trovarci nella situazione in cui se un comando ha un esito devono essere eseguite delle istruzioni, altrimenti devono esserne eseguite altre. Bene, in questa situazione ci viene in aiuto l'istruzione else, quindi il costrutto assume la forma:

if (CONDIZIONE); then

    (BLOCCO1);

else

    (BLOCCO2);

fi

Lo script precedente può essere così modificato:

#!/bin/bash
è
clear

utente="qwerty";

echo -e "Inizio script\n";

if [[ -e /etc/passwd ]]; then

        echo "File /etc/passwd esistente";

else

        echo "Il file /etc/passwd non esiste!!";

fi

if grep -q $utente  /etc/passwd; then

        echo "Utente $utente verificato";

else

        echo "L'utente $utente non esiste!!";

fi

echo -e "\nFine script";

In questo caso, se uno dei comandi dà esito negativo, questo viene segnalato. Ho aggiunto una variabile che contiene il nome dell'utente in modo da evidenziare proprio questo comportamento, a meno che nel vostro sistema non abbiate davvero l'utente qwerty, lo script eseguirà il secondo ramo else.

Ultima cosa da dire sull'istruzione if, è possibile annidare più if, in modo da creare costrutti più complessi, in questo caso si introduce l'istruzione elif, arrivando alla seguente sintassi:

if (CONDIZIONE1); then

    (BLOCCO1);

elif (CONDIZIONE2); then

    (BLOCCO2);

elif (CONDIZIONE3); then

    (BLOCCO3);

else

    (BLOCCO4);

fi

Ovviamente la complessità può essere anche maggiore. Giusto per fare un esempio, ecco uno script con un costrutto if complesso:

#!/bin/bash

clear

numero=$1;

echo -e "Inizio script\n";è

if [[ $numero == uno ]]; then

        echo "Hai digitato uno";

elif [[ $numero == due ]]; then

        echo "Hai digitato due";

elif [[ $numero == tre ]]; then

        echo "Hai digitato tre";

else

        echo "Altro numero diverso da uno, due e tre";

fi

echo -e "\nFine script";

Provate a lanciare lo script, chiamato ad esempio num.sh, con ./num.sh due, e vedrete il funzionamento del costrutto messo in piedi.

Ovviamente, un costrutto di questo tipo può diventare estremamente complesso al crescere dell'annidamento. Per ovviare a questo, esiste l'istruzione case, che ha la seguente sintassi:

case (VARIABILE) in

    ( CONDIZIONE1 )

        (BLOCCO1);;

    ( CONDIZIONE2 )

        (BLOCCO2);;

    ( CONDIZIONE3 )

        (BLOCCO3)

    ...

    ( * )

        ( BLOCCO4)

esac

Lo script precedente, il cui funzionamento resta immutato, diventa:

!/bin/bash

clear

numero=$1;

echo -e "Inizio script\n";

case $numero in

        ( uno )

                echo "Hai digitato uno";;

        ( due )

                echo "Hai digitato due";;

        ( tre )

                echo "Hai digitato tre";;

        ( * )

                echo "Altro numero diverso da uno, due e tre"

esac

echo -e "\nFine script";

Da notare l'ultima voce, contrassegnata da ( * ), indica il default, ovvero ciò che deve essere eseguito se non si ricade in alcuno dei casi precedentemente indicati. Attenzione perchè anche in questo caso gli spazi sono obbligatori!!

Alla prossima..