Press "Enter" to skip to content

If else in bash – guida completa con esempi

Il costrutto if then else è uno dei costrutti fondamentali di tutti i linguaggi di programmazione, si tratta infatti di un costrutto che permette di eseguire un processo decisionale all’interno del codice. Permette quindi al nostro programma di non essere sempre eseguito nello stesso modo, ma di prendere delle decisioni in base al contesto della singola esecuzione. Ovviamente saremo noi a dover istruire il nostro programma su come prendere le decisioni. Il costrutto if else in bash serve proprio a questo, ovvero a guidare l’esecuzione dello script in base al verificarsi o meno di determinate condizioni.

Vediamo quindi come si usa l’if in bash.

Sommario

If bash – sintassi e primo esempio base

La condizione if in bash, a differenza di altri linguaggi di programmazione come java, non utilizza le parentesi per delimitare il blocco di codice a cui si riferisce, ma utilizza la parola chiave fi. La sintassi di base del comando if then ha la seguente forma:

if <condizione>
then
   <azione>
fi

Questa è la sintassi base del comando bash if, in questo caso viene testata la condizione, se questa è vera viene eseguito il codice contenuto nel blocco <azione>, altrimenti il blocco viene saltato.

La condizione ovviamente può basarsi su input esterni o valori che possono cambiare nelle varie esecuzioni dello script, questo fa si che la condizione non sia staticamente sempre vera o sempre falsa.

Esempio pratico

Vediamo uno script di esempio molto semplice per capire come funziona il costrutto if.

#!/bin/bash

if [ $1 -gt 100 ]
then
   echo Hai inserito un numero grande.
   pwd
fi

date

Vediamo di analizzare questo piccolo script punto per punto

  • La prima riga, come probabilmente sapete, serve a specificare che si tratta di uno script bash
  • La secondo riga è la prima parte dell’istruzione if, in questo caso testiamo che il primo argomento passato al momento dell’esecuzione dello script ( identificato da $1) è maggiore di (-gt) 100.
  • Le due righe successive rappresentano il codice da eseguire se la condizione testata è vera. In questo blocco possiamo inserire quante righe vogliamo.
  • La parola chiave fi indica che il blocco if è terminato. Tutto quello che è dopo questa riga sarà eseguito indipendentemente dal valore della condizione dell’if.
  • L’ultima riga rappresenta un comando che verrà eseguito a prescindere dall’istruzione if.

Salviamo il codice in un file if_base.sh e lanciamolo sul nostro terminale con diversi argomenti per vedere le differenze.

output comando if bash base
Output dello script bash if_base.sh

Come possiamo osservare, nella prima esecuzione abbiamo fornito un parametro minore di 100, in questo caso è stata stampata solamente la data, ovvero l’istruzione che si trovava fuori dal blocco if.
Nella seconda esecuzione abbiamo utilizzato 150, un valore più alto di 100, la condizione è quindi stata testata con esito true. Di conseguenza sono stati eseguiti i comandi all’interno del blocco then e poi il comando date che, come abbiamo detto, viene eseguito in ogni caso.

Operatori di test

Nell’esempio precedente abbiamo usato l’espressione [ $1 -gt 100 ] per testare se il parametro in input fosse più grande o meno di 100. In questo caso le parentesi quadre ( [] ) sono un riferimento al comando bash test. Il comando test serve appunto per testare una condizione, e dispone di una moltitudine di operatori che saranno quindi utilizzabili anche per definire la condizione if.

Possiamo fare riferimento alla pagina man test per avere tutti i dettagli sugli operatori disponibili, ma vediamo in una tabella riassuntiva quelli più comunemente utilizzati.

OperatoreDescrizione
!<espressione>Operatore logico not, restituisce il contrario di <espressione>
-n <stringa>Vero se la lunghezza della stringa <stringa> è maggiore di zero
-z <stringa>Vero se la stringa <stringa> è vuota (lunghezza 0)
<stringa1> = <stringa2>Verifica se <stringa1> e <stringa2> sono uguali
<stringa1> != <stringa2>Verifica se <stringa1> e <stringa2> non sono uguali
<intero1> -eq <intero2>Restituisce true se <intero1> e <intero2> sono numericamente uguali
<intero1> -gt <intero2>Restituisce true se <intero1> è maggiore di <intero2>
<intero1> -lt <intero2>Restituisce true se <intero1> è minore di <intero2>
<intero1> -ge <intero2>Restituisce true se <intero1> è maggiore o uguale di <intero2>
<intero1> -le <intero2>Restituisce true se <intero1> è minore o uguale di <intero2>
-h <file>Verifica se il file <file> esiste ed è un collegamento simbolico
-r <file>Verifica se il file <file> esiste ed è leggibile dall’utente
-w <file>Verifica se il file <file> esiste ed è scrivibile dall’utente
-x <file>Verifica se il file <file> esiste ed è eseguibile
-d <file>Verifica se il file <file> esiste ed è una directory
-e <file>Verifica se il file <file> esiste senza considerare il tipo (directory, file normale, ecc..)
-f <file>Verifica se il file <file> esiste ed è un file normale (non una directory)
-s <file>Verifica se il file <file> esiste e non è vuoto
operatori del comando test

A questa tabella aggiungiamo qualche osservazione

  • Gli operatori = e -eq sono leggermente differenti, prendiamo ad esempio il caso [002 = 2]. In questo caso, utilizzando l’operatore = effettuiamo una comparazione fra stringhe carattere per carattere, il risultato sarà quindi false. Se invece scriviamo [002 -eq 2] avremo una comparazione numerica, il risultato sarà quindi true in quanto stiamo parlando sempre del numero 2.
  • L’argomento <file> è da intendersi più largamente come percorso. Un percorso può essere assoluto o relativo e può riferirsi a un file o a una directory.

Visto che le parentesi quadre [] nella condizione if bash sono un riferimento al comando test, possiamo utilizzare questo comando direttamente dal terminale per controllare il corretto funzionamento delle espressioni che scriveremo.

Proviamo ad usare il comando test in congiunzione con il comando echo $? per verificare di aver compreso bene l’uso di alcuni operatori

esempi comando bash test
Esempi di comando test

Per questi esempi abbiamo utilizzato il comando test seguito da echo $?, quest’ultimo comando ci restituisce l’exit code dell’ultimo comando eseguito. Se otteniamo 1 il comando test ha restituito false, se otteniamo 0, ha restituito true.

Nel primo esempio abbiamo testato l’operatore =applicato a 001 e 1. Come avevamo detto nelle osservazioni, in questo caso il test ritorna false in quanto l’operatore esegue il confronto tra stringhe carattere per carattere.

Nel secondo esempio invece abbiamo testato l’operatore -eq con gli stessi valori. In questo caso, come ci aspettavamo, la comparazione è stata fatta sui valori numerici e quindi il test ha dato esito true.

Nel terzo esempio abbiamo creato un file vuoto tramite il comando touch, per poi testarlo con l’operatore -s. In questo caso avendo appena creato un file vuoto, l’operazione test -s da esito false.

Nell’ultimo esempio abbiamo riempito il file precedentemente creato con l’output di ls /etc per poi testarlo di nuovo con il comando test -s. Questa volta il risultato è true in quanto il file esiste e non è vuoto.

Quando andremo a riportare le espressioni negli script bash dovremo eliminare il comando test e sostituirlo racchiudendo tutta l’espressione fra parentesi quadre [<espressione>]. Le versioni più recenti supportano anche l’espressione fra parentesi quadre doppie [[<espressione>]], sintassi che permette in alcuni casi di non dover racchiudere fra virgolette variabili che potrebbero contenere degli spazi.

If annidati

Se serve è possibile creare if annidati in bash, ovvero inserire un blocco if dentro un altro.
Se volessimo eseguire ulteriori analisi sul numero inserito nel codice dell’esempio base visto in precedenza, possiamo aggiungere degli if nidificati e testare ulteriori condizioni.

#!/bin/bash

if [ $1 -gt 100 ]
then
   echo Hai inserito un numero grande.
   if (( $1 % 2 == 0))
   then
      echo E il numero è anche pari.
   fi
fi

In questo caso abbiamo prima testato che il numero ricevuto sia più grande di 100 e abbiamo eseguito il primo comando echo per avvertire l’utente che il numero inserito è grande.
Subito dopo, ma rimanendo nell’ambito del codice da eseguire se è verificata la condizione [ $1 -gt 100 ] abbiamo aperto un altro comando if, testando questa volta che il numero sia pari attraverso l’espressione (( $1 % 2 == 0)). Nel ramo then abbiamo quindi messo un ulteriore echo per avvisare che il numero inserito è pari. Questa istruzione viene quindi eseguita solamente se entrambe le condizioni sono verificate. Le due successive parole chiave fi servono a delimitare la fine dell’if più interno la prima e quello più esterno la seconda.

In via teorica non ci sono limiti al numero di istruzioni if che possono essere annidate, ma se questo numero dovesse crescere troppo potrebbe essere necessario ricontrollare il codice per capire se può essere organizzato in maniera migliore.

If else

In molti casi può essere necessario eseguire due distinte porzioni di codice a seconda che una condizione sia vera o falsa. In questo caso possiamo utilizzare if else in bash come in molti altri linguaggi di programmazione.

Vediamo brevemente la sintassi del comando if else, chiamato anche if then else.

if [<condizione>]
then
   <azione se vero>
else
   <azione se falso>
fi

In questo caso il comando if testerà la <condizione>, se questa darà esito true, eseguirà la porzione di codice <azione se vero>, se invece il test darà esito false verrà eseguita la porzione di codice <azione se falso>. In entrambi i casi il blocco che non viene eseguito verrà ignorato come se non fosse presente nello script.

If elif else

In alcuni casi è possibile che sia necessario eseguire una serie di controlli consecutivi e ogni volta eseguire il test successivo solamente se quelli prima hanno dato esito false. Questo permette di eseguire un pezzo di codice solamente se è soddisfatta una condizione e non erano soddisfatte tutte le precedenti. La sintassi in questo caso è la seguente:

if [<condizione A>]
then
   <azione se vero A>
elif [<condizione B>]
then
   <azione se vero B ma non era vero A>
else
   <azione se tutti i controlli precedenti erano falsi>
fi

In questo costrutto testiamo la <condizione A>, se questa è vera viene eseguito il blocco then <azione se vero A> e una volta fatto questo il programma esce dall’istruzione if senza testare ulteriori condizioni. Se la <condizione A> ritorna un valore false, viene testata la <condizione B>, se questa è vera viene eseguito il blocco then <azione se vero B ma non era vero A> e una volta fatto questo il programma esce dall’istruzione if senza testare ulteriori condizioni. Se anche il test di <condizione B> da esito false, viene eseguito il codice nel blocco else che in questa situazione ha significato di “in tutti gli altri casi”.

Facciamo un esempio pratico. Abbiamo bisogno di decidere se una persona può accedere a una festa. Il nostro script riceverà come argomenti l’età di una persona e “si” o “no” a seconda che abbia una lettera di autorizzazione firmata dai genitori. Le condizioni per entrare sono che è possibile accedere alla festa se si è maggiorenni, oppure se si è minorenni ma in possesso della lettera di autorizzazione. In quest’ultimo caso è necessario lasciare la festa prima di mezzanotte.

Vediamo il nostro script:

#!/bin/bash
if [ $1 -ge 18 ]
then
   echo Puoi entrare liberamente alla festa.
elif [ $2 == 'si' ]
then
   echo Puoi entrare alla festa ma devi uscire entro mezzanotte.
else
   echo Non puoi entrare alla festa.
fi

Nel costrutto if elif else è possibile avere più di un blocco elif, e possiamo omettere il blocco else nel caso non sia necessario eseguire codice se tutte le condizioni sono false.

Operatori booleani per condizioni multiple AND e OR

A volte può essere necessario testare più di una condizione nel costrutto if, e considerare il test true se entrambe le condizioni sono true oppure se almeno una delle due condizioni è true. Possiamo in questo caso utilizzare gli operatori boolean fra condizioni. la sintassi degli operatori AND e OR è la seguente:

  • AND – &&
  • OR – ||

Questi operatori possono essere utili nel caso volessimo testare 2 caratteristiche di un file passato come argomento:

#!/bin/bash

if [ -r $1 ] && [ -s $1 ]
then
   echo Questo file è sia leggibile che non vuoto
fi

In questo esempio testiamo che il file passato come argomento sia leggibile dall’utente ( -r ) e al contempo non sia vuoto ( -s ). Se entrambe le condizioni viene eseguito il codice all’interno del blocco then, altrimenti lo script continua normalmente senza eseguire il blocco.

Vediamo un altro esempio con l’operatore OR

#!/bin/bash

if [ $USER = 'Alice' ] && [ $USER = 'Bruno' ]
then
   echo Benvenuto
else
   echo Non sei Alice o Bob quindi non sei il benvenuto qui
fi

In questo esempio diamo il benvenuto solamente se l’utente è Alice o Bob, tutti gli altri utenti non saranno i benvenuti nel nostro sistema.

Il costrutto case

A volte abbiamo una variabile che può assumere una serie di valori, e vogliamo eseguire del codice diverso per ogni valore, o almeno per alcuni di questi. Potremmo realizzare questo intento utilizzando una serie di if elif, ma al crescere dei casi diminuirebbe la leggibilità del codice. Per queste situazioni esiste il costrutto case, che permette di gestire tutto generando codice più pulito e ordinato.

Non è facile illustrare a parole il funzionamento del costrutto case in bash, lo faremo quindi attraverso uno schema della sintassi e qualche esempio.

case <variabile> in
<espressione 1>)
   <azione se espressione 1>
   ;;
<espressione 2>)
   <azione se espressione 2>
   ;;
esac

Vediamo un semplice esempio pratico

#!/bin/bash

case $1 in
   start)
      echo sto avviando
      ;;
   stop)
      echo sto fermando
      ;;
   restart)
      echo sto riavviando
      ;;
   *)
      echo non so cosa fare comando non riconosciuto
   ;;
esac

In questo esempio come possiamo intuire prendiamo in esame il valore del primo argomento passato allo script ( $1 ). Se è uguale a start eseguiamo il blocco echo sto avviando. Se è uguale a stop eseguiamo il blocco echo sto fermando e così via. L’espressione * indica qualsiasi valore, solitamente si utilizza alla fine per definire cosa fare nel caso il valore non venga riconosciuto da nessuna delle condizioni precedenti.

Nel costrutto la ) viene usata per indicare che il pattern di riconoscimento è terminato.
Incontriamo per la prima volta anche il doppio punto e virgola ( ;; ) che indica che il codice di quel blocco case è terminato e che quindi segue il prossimo pattern da valutare.
La parola chiave esac ( case al contrario), come per la parola chiave fi, indica che il blocco case è terminato e il codice che segue sarà eseguito normalmente.

Le espressioni usate nei vari pattern possono essere anche più complesse come nell’esempio seguente:

#!/bin/bash

spazio_usato=$( df -h | awk '{ print $5 }' | sort -n | tail -n 1 | sed 's/%//' )
case $spazio_usato in
   [1-5]*)
      echo Molto spazio libero
      ;;
   [6-7]*)
      echo Lo spazio libero non è moltissimo
      ;;
   8*)
      echo Credo sia ora di iniziare a cancellare qualcosa
      ;;
   9*)
      echo Avremo problemi di spazio a breve
      ;;
   *)
      echo Qualcosa non va, percentuale di spazio libero non riconosciuta
      ;;
esac

In questo caso mostreremo messaggi diversi in caso lo spazio libero nel sistema sia molto (usato 1-59%), medio (usato 60-79%), poco (usato 80-89%) e pochissimo (usato 90-99%).

Conclusioni

Siamo giunti al termine di questa analisi del comando if bash, abbiamo visto come scrivere i costrutti if, if else, if elif else, e il costrutto case. Se doveste avere ancora qualche dubbio non esitate a commentare!

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.