Guida avanzata di scripting Bash: Un'approfondita esplorazione dell'arte dello scripting di shell | ||
---|---|---|
Indietro | Capitolo 10. Cicli ed alternative | Avanti |
I costrutti case e select, tecnicamente parlando, non sono cicli, dal momento che non iterano l'esecuzione di un blocco di codice. Come i cicli, tuttavia, hanno la capacità di dirigere il flusso del programma in base alle condizioni elencate dall'inizio alla fine del blocco.
Controllo del flusso del programma in un blocco di codice
Il costrutto case è l'equivalente di scripting di shell di switch del C/C++. Permette di dirigere il flusso del programma ad uno dei diversi blocchi di codice, in base alle condizioni di verifica. È una specie di scorciatoia di enunciati if/then/else multipli e uno strumento adatto per creare menu.
case "$variabile" in
"$condizione1" )
comando...
;;
"$condizione2" )
comando...
;;
esac
|
Esempio 10-24. Impiego di case
#!/bin/bash # Verificare intervalli di caratteri. echo; echo "Premi un tasto e poi invio." read Tasto case "$Tasto" in [[:lower:]] ) echo "Lettera minuscola";; [[:upper:]] ) echo "Lettera maiuscola";; [0-9] ) echo "Cifra";; * ) echo "Punteggiatura, spaziatura, o altro";; esac # Sono permessi gli intervalli di caratteri se #+ compresi tra [parentesi quadre] #+ o nel formato POSIX tra [[doppie parentesi quadre. # La prima versione di quest'esempio usava, per indicare #+ gli intervalli di caratteri minuscoli e maiuscoli, le forme #+ [a-z] e [A-Z]. # Questo non è più possibile nel caso di particolari impostazioni #+ locali e/o distribuzioni Linux. # POSIX consente una maggiore portabilità. # Grazie a Frank Wang per averlo evidenziato. # Esercizio: # --------- # Così com'è, lo script accetta la pressione di un solo tasto, quindi #+ termina. Modificate lo script in modo che accetti un input continuo, #+ visualizzi ogni tasto premuto e termini solo quando viene digitata una "X". # Suggerimento: racchiudete tutto in un ciclo "while". exit 0 |
Esempio 10-25. Creare menu utilizzando case
#!/bin/bash # Un database di indirizzi non molto elegante clear # Pulisce lo schermo. echo " Elenco Contatti" echo " ------ --------" echo "Scegliete una delle persone seguenti:" echo echo "[E]vans, Roland" echo "[J]ones, Mildred" echo "[S]mith, Julie" echo "[Z]ane, Morris" echo read persona case "$persona" in # Notate l'uso del "quoting" per la variabile. "E" | "e" ) # Accetta sia una lettera maiuscola che minuscola. echo echo "Roland Evans" echo "4321 Floppy Dr." echo "Hardscrabble, CO 80753" echo "(303) 734-9874" echo "(303) 734-9892 fax" echo "revans@zzy.net" echo "Socio d'affari & vecchio amico" ;; # Attenzione al doppio punto e virgola che termina ogni opzione. "J" | "j" ) echo echo "Mildred Jones" echo "249 E. 7th St., Apt. 19" echo "New York, NY 10009" echo "(212) 533-2814" echo "(212) 533-9972 fax" echo "milliej@loisaida.com" echo "Ex fidanzata" echo "Compleanno: Feb. 11" ;; # Aggiungete in seguito le informazioni per Smith & Zane. * ) # Opzione predefinita. # Un input vuoto (tasto INVIO) o diverso dalle scelte #+ proposte, viene verificato qui. echo echo "Non ancora inserito nel database." ;; esac echo # Esercizio: # --------- # Modificate lo script in modo che accetti input multipli, #+ invece di terminare dopo aver visualizzato un solo indirizzo. exit 0 |
Un uso particolarmente intelligente di case è quello per verificare gli argomenti passati da riga di comando.
#!/bin/bash case "$1" in "") echo "Utilizzo: ${0##*/} <nomefile>"; exit $E_ERR_PARAM;; # Nessun parametro da riga di comando, # o primo parametro vuoto. # Notate che ${0##*/} equivale alla sostituzione di parametro #+ ${var##modello}. Cioè $0. -*) NOMEFILE=./$1;; # Se il nome del file passato come argomento #+ ($1) inizia con un trattino, lo sostituisce #+ con ./$1 di modo che i comandi successivi #+ non lo interpretino come un'opzione. * ) NOMEFILE=$1;; # Altrimenti, $1. esac |
Ecco un esempio ancor più chiaro di gestione dei parametri passati da riga di comando:
#! /bin/bash while [ $# -gt 0 ]; do # Finché ci sono parametri . . . case "$1" in -d|--debug) # "-d" o "--debug" parametro? DEBUG=1 ;; -c|--conf) FILECONF="$2" shift if [ ! -f $FILECONF ]; then echo "Errore: il file indicato non esiste!" exit $E_ERR_FILECONF # Errore di file non trovato. fi ;; esac shift # Verifica la serie successiva di parametri. done # Dallo script "Log2Rot" di Stefano Falsetto, #+ parte del suo pacchetto "rottlog". # Usato con il consenso dell'autore. |
Esempio 10-26. Usare la sostituzione di comando per creare la variabile di case
#!/bin/bash # case-cmd.sh: usare la sostituzione di comando per creare la variabile #+ di "case". case $( arch ) in # "arch" restituisce l'architettura della macchina. # Equivale a 'uname -m'... i386 ) echo "Macchina con processore 80386";; i486 ) echo "Macchina con processore 80486";; i586 ) echo "Macchina con processore Pentium";; i686 ) echo "Macchina con processore Pentium2+";; * ) echo "Altro tipo di macchina";; esac exit 0 |
Un costrutto case può filtrare le stringhe in una ricerca che fa uso del globbing.
Esempio 10-27. Una semplice ricerca di stringa
#!/bin/bash # match-string.sh: semplice ricerca di stringa verifica_stringa () { UGUALE=0 NONUGUALE=90 PARAM=2 # La funzione richiede 2 argomenti. ERR_PARAM=91 [ $# -eq $PARAM ] || return $ERR_PARAM case "$1" in "$2") return $UGUALE;; * ) return $NONUGUALE;; esac } a=uno b=due c=tre d=due verifica_stringa $a # numero di parametri errato echo $? # 91 verifica_stringa $a $b # diverse echo $? # 90 verifica_stringa $b $d # uguali echo $? # 0 exit 0 |
Esempio 10-28. Verificare un input alfabetico
#!/bin/bash # isalpha.sh: Utilizzare la struttura "case" per filtrare una stringa. SUCCESSO=0 FALLIMENTO=-1 isalpha () # Verifica se il *primo carattere* della stringa #+ di input è una lettera. { if [ -z "$1" ] # Nessun argomento passato? then return $FALLIMENTO fi case "$1" in [a-zA-Z]*) return $SUCCESSO;; # Inizia con una lettera? * ) return $FALLIMENTO;; esac } # Confrontatelo con la funzione "isalpha ()" del C. isalpha2 () # Verifica se l'*intera stringa* è composta da lettere. { [ $# -eq 1 ] || return $FALLIMENTO case $1 in *[!a-zA-Z]*|"") return $FALLIMENTO;; *) return $SUCCESSO;; esac } isdigit () # Verifica se l'*intera stringa* è formata da cifre. { # In altre parole, verifica se è una variabile numerica. [ $# -eq 1 ] || return $FALLIMENTO case $1 in *[!0-9]*|"") return $FALLIMENTO;; *) return $SUCCESSO;; esac } verifica_var () # Front-end per isalpha (). { if isalpha "$@" then echo "\"$*\" inizia con un carattere alfabetico." if isalpha2 "$@" then # Non ha significato se il primo carattere non è alfabetico. echo "\"$*\" contiene solo lettere." else echo "\"$*\" contiene almeno un carattere non alfabetico." fi else echo "\"$*\" non inizia con una lettera." # Stessa risposta se non viene passato alcun argomento. fi echo } verifica_cifra ()# Front-end per isdigit (). { if isdigit "$@" then echo "\"$*\" contiene solo cifre [0 - 9]." else echo "\"$*\" contiene almeno un carattere diverso da una cifra." fi echo } a=23skidoo b=H3llo c=-Cosa? d=Cosa? e=`echo $b` # Sostituzione di comando. f=AbcDef g=27234 h=27a34 i=27.34 verifica_var $a verifica_var $b verifica_var $c verifica_var $d verifica_var $e verifica_var $f verifica_var # Non viene passato nessun argomento, cosa succede? # verifica_cifra $g verifica_cifra $h verifica_cifra $i exit 0 # Script perfezionato da S.C. # Esercizio: # --------- # Scrivete la funzione 'isfloat ()' che verifichi i numeri in virgola #+ mobile. Suggerimento: la funzione è uguale a 'isdigit ()', ma con #+ l'aggiunta della verifica del punto decimale. |
Il costrutto select, adottato dalla Shell Korn, è anch'esso uno strumento per creare menu.
select variabile [in lista]
do
comando...
break
done
Viene visualizzato un prompt all'utente
affinché immetta una delle scelte presenti nella
variabile lista. Si noti che select usa,
in modo predefinito, il prompt PS3
(#? ). Questo può essere
modificato.
Esempio 10-29. Creare menu utilizzando select
#!/bin/bash PS3='Scegli il tuo ortaggio preferito: '# Imposta la stringa del prompt. echo select verdura in "fagioli" "carote" "patate" "cipolle" "rape" do echo echo "Il tuo ortaggio preferito sono i/le $verdura." echo "Yuck!" echo break # Cosa succederebbe se non ci fosse il "break"? done exit 0 |
Se viene omesso
in lista
allora select usa l'elenco degli
argomenti passati da riga di comando allo script
($@
) o alla funzione in cui il costrutto
select è inserito.
Lo si confronti con il comportamento del costrutto
for variabile [in lista]
con in lista omesso.Esempio 10-30. Creare menu utilizzando select in una funzione
#!/bin/bash PS3='Scegli il tuo ortaggio preferito: ' echo scelta_di() { select verdura # [in lista] omesso, quindi 'select' usa gli argomenti passati alla funzione. do echo echo "Il tuo ortaggio preferito: $verdura." echo "Yuck!" echo break done } scelta_di fagioli riso carote ravanelli pomodori spinaci # $1 $2 $3 $4 $5 $6 # passati alla funzione scelta_di() exit 0 |
Vedi anche Esempio 34-3.