Capitolo 7. Verifiche

Sommario
7.1. Costrutti condizionali
7.2. Operatori di verifica di file
7.3. Altri operatori di confronto
7.4. Costrutti condizionali if/then annidati
7.5. Test sulla conoscenza delle verifiche

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.

7.1. Costrutti condizionali

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

Nota

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.

if [ -x "$nome_file" ]; then

Else if ed elif

elif

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.

Nota

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.

bash$ type test
test is a shell builtin
bash$ type '['
[ is a shell builtin
bash$ type '[['
[[ is a shell keyword
bash$ type ']]'
]] is a shell keyword
bash$ type ']'
bash: type: ]: not found
	      

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.

Nota

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

Suggerimento

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 [ ] .

Nota

Dopo un if non sono strettamente necessari né il comando test né i costrutti parentesi quadre ( [ ] o [[ ]] ).

dir=/home/bozo

if cd "$dir" 2>/dev/null; then # "2>/dev/null" sopprime il messaggio d'errore.
  echo "Ora sei in $dir."
else
  echo "Non riesco a cambiare in $dir."
fi
Il costrutto "if COMANDO" restituisce l'exit status di COMANDO.

Per questo motivo, una condizione tra parentesi quadre può essere utilizzata da sola, senza if, se abbinata ad un costrutto lista.

var1=20
var2=22
[ "$var1" -ne "$var2" ] && echo "$var1 è diversa da $var2"

home=/home/bozo
[ -d "$home" ] || echo "La directory $home non esiste."

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