Costrutti di Test
Stato di ritorno dei comandi
Ogni comando ritorna alla shell chiamante un numero intero, lo stato di ritorno, di valore:
0
- se il comando ha avuto successo>0
- se il comando è fallito
Lo stato di ritorno dell'ultimo comando eseguito è contenuto nella variabile $?
Esempio:
ls /etc/passwd
/etc/passwd
echo $?
0
ls /etc/password
… no such …
echo $?
2
Costrutto if
La sintassi è:
if comando
then
istruzioni
else
istruzioni
fi
Notare che l'argomento di if non è una codizione logica booleana, come in altri linguaggi, ma un comando.
if
esegue il comando e ne testa lo stato di ritorno
- Se ha avuto successo compie le istruzioni dopo il
then
- Se è fallito compie le istruzioni dopo lo
else
- La branca
else
è opzionale - E' terminato da
fi
L'indentazione non è necessaria, ma è raccomandata per chiarezza visiva
Esempio.
(if1.sh)
#! /bin/bash
if ls $1 > /dev/null 2>&1
then
echo "Il file $1 esiste"
else
echo "il file $1 non esiste"
fi
Provare con vari test:
./if1.sh if1.sh
Il file if1.sh esiste
./if1.sh zippo
il file zippo non esiste
./if1.sh if*
Il file if1.sh esiste
Note
> /dev/null
ridirige lo standard output sul device nullo, scarta l'output del comando
2>&1
ridirige lo standard error in merge con lo standard output, scarta gli errori del comando
Nel caso di ./if1.sh if*
la shell espande il carattere jolly *
prima dell'esecuzione del comando
Se vi sono più file soddisfatti dal carattere jolly, solo il primo
viene mostrato.
Notare il caso limite:
./if1.sh
Il file esiste
Occorre testare che vi siano argomenti prima di eseguire il resto della procedura
Il comando test
if
esegue un comando
- Normalmente gli if hanno una condizione logica
Il comando test
ha come argomenti un'espressione logica
- test ha successo se la condizione è vera
- fallisce se la condizione è falsa
Tramite test si può far provare a if
una condizione logica
Rivisitazione dell'esempio precedente:
(if2.sh)
#! /bin/bash
if test $# -eq 0
then
echo "$0: usage: $0 file" 1>&2
exit 1
fi
if ls $1 > /dev/null 2>&1
then
echo "Il file $1 esiste"
else
echo "il file $1 non esiste"
fi
Notare la filosofia: se qualcuno degli ingredienti necessari alla procedura non è presente, allora si termina subito con un messaggio d'errore.
Il comando exit 1
esce dalla procedura e ritorna lo stato di ritorno 1
alla shell chiamante.
In altre parole: non speriamo di avere tutto per poter continuare a lavorare; speriamo che qualcosa manchi così non si deve lavorare.
Oltre che
if test cond_logica
si può scrivere
if [ cond_logica ]
Notare gli spazi obbligatori vicino alle quadre.
Questo è cosiddetto zucchero sintattico per rendere l'aspetto più simile a sintassi tradizionale. Quello che succede internamente è che [
è un alias del comando test
.
Esempio.
(test1.sh)
#! /bin/bash
echo -n "Dammi un numero: "
read num
if test $num -gt 5
then
echo "Il numero e' maggiore di cinque"
else
echo "E' un numero piccolo"
fi
Operatori di test
Operatori di paragone numerico:
-gt
- maggiore-ge
- maggiore o uguale-lt
- minore-le
- minore o uguale-eq
- uguale-ne
- non uguale
Operatori di paragone tra stringhe:
"str1" = "str2"
- uguali"str1" != "str2"
- diverse
Esempio.
(test2.sh)
#! /bin/bash
if [ "$(whoami)" = "root" ]
then
echo "Attenzione: sei l'amministratore"
else
echo "Non sei l'amministratore"
fi
Altro esempio, basato sul fatto che la variabile d'ambiente $UID contiene lo User Id, e che se questo è 0 abbiamo i poteri del supruser, ossia dell'amministratore.
(test3.sh)
#! /bin/bash
warn=Attenzione:
non=Non
if [ $UID -eq 0 ]
then
unset non
else
unset warn
fi
echo "$warn $non sei l'amministratore"
Il comando unset
seguito dal nome di una variabile, toglie quella variabile se esiste, e non da errore se non esiste.
Serve per assicurarsi che una variabile non sia settata.
La stampa di una variabile non settat aproduce uno spazio vuoto (non la stringa nulla).
Altre opzioni di test
Il comando test
serve anche a testare le proprietà di un file:
-e file
- esiste-f file
- esiste ed è un file regolare-d file
- esiste ed è una directory-L file
- esiste ed è un link simbolico-r file
- esiste ed ha il permesso di lettura-w file
- esiste ed ha il permesso di scrittura-x file
- esiste ed ha il permesso di esecuzione-u file
- esiste ed è un SUID-g file
- esiste ed è uno SGID-k file
- esiste ed ha il POSIX bit-s file
- esiste e ha dimensione maggiore di zero-b file
- esiste ed è un device a blocchi-c file
- esiste ed è un device a caratteri-p file
- esiste ed è una pipe-S file
- esiste ed è un socket
I permesi sono intesi per l'utente che sta eseguendo la procedura corrente.
E ancora:
-O file
- siamo i possessori del file-G file
- il file è del nostro gruppofile1 -nt file2
- file1 è più nuovo di file2 (modifica)file1 -ot file2
- file1 è più vecchio di file2file1 -ef file2
- file1 e file2 sono link diretti allo stesso inode
Più test diversi si possono unire con operatori logici:
!
- NOT-a
- AND-o
- OR
Anche test di stringhe:
-z $var
- testa sevar
è una stringa vuota-n $var
testa sevar
è una stringa non vuota
Nel test di una stringa con if
il costrutto:
if [ "$str1" = "" ]
causa errore. Meglio scivere:
if [ "x$str1" = "x" ]
oppure meglio ancora:
if [ -z "$str1" ]
Il Costrutto case
Sintassi:
case variabile in
cost1 )
istruzioni
istruzioni ;;
cost2 | cost3 )
istruzioni ;;
. . .
* )
istruzioni ;;
esac
case
confronta la variabile con ogni costante
Al primo match esegue le istruzioni che seguono fino al ;;
poi esce dopo esac
(si, è case a rovescio, cos' come fi è if arovescio)
La costante *
è sempre soddisfatta
e se c'è deve essere l'ultima della lista
Esempio.
(case1.sh)
#! /bin/bash
cat << EOF
Unix e':
1) Un bellissimo sistema operativo
2) Un'agenzia ONU
3) Un club di bagni turchi
EOF
echo -n "Scelta? "
read sc
case $sc in
1 )
echo "Bravo!"
echo "Hai studiato" ;;
2|3 )
echo "No!" ;;
*)
echo "Scelta non prevista" ;;
esac
Note
2|3
denota un OR
Piccola miglioria, il comando interno read
è più complesso di quanto si creda (RTFM: help read).
Sostituire le linee:
echo -n "Scelta? "
read sc
con la linea
read -sn 1 -p "Scelta? " sc
ove:
-s
- silenzioso-n 1
- legge solo un carattere-p "..."
- pronto