| Guida avanzata di scripting Bash: Un'approfondita esplorazione dell'arte dello scripting di shell | ||
|---|---|---|
| Indietro | Capitolo 10. Cicli ed alternative | Avanti |
Comandi inerenti al comportamento del ciclo
I comandi di controllo del ciclo break e continue [1] corrispondono esattamente ai loro analoghi degli altri linguaggi di programmazione. Il comando break interrompe il ciclo (esce), mentre continue provoca il salto all'iterazione (ripetizione) successiva, tralasciando tutti i restanti comandi di quel particolare passo del ciclo.
Esempio 10-20. Effetti di break e continue in un ciclo
#!/bin/bash
LIMITE=19 # Limite superiore
echo
echo "Visualizza i numeri da 1 fino a 20 (saltando 3 e 11)."
a=0
while [ $a -le "$LIMITE" ]
do
a=$(($a+1))
if [ "$a" -eq 3 ] || [ "$a" -eq 11 ] # Esclude 3 e 11.
then
continue # Salta la parte restante di questa particolare
#+ iterazione del ciclo.
fi
echo -n "$a " # Non visualizza 3 e 11.
done
# Esercizio:
# Perché il ciclo visualizza fino a 20?
echo; echo
echo Visualizza i numeri da 1 a 20, ma succede qualcosa dopo il 2.
##################################################################
# Stesso ciclo, ma sostituendo 'continue' con 'break'.
a=0
while [ "$a" -le "$LIMITE" ]
do
a=$(($a+1))
if [ "$a" -gt 2 ]
then
break # Salta l'intero ciclo.
fi
echo -n "$a "
done
echo; echo; echo
exit 0 |
Il comando break può avere
un parametro. Il semplice break
conclude il ciclo in cui il comando di trova, mentre un
break N interrompe il ciclo di livello
N.
Esempio 10-21. Interrompere un ciclo ad un determinato livello
#!/bin/bash
# break-levels.sh: Interruzione di cicli.
# "break N" interrompe i cicli al livello N.
for cicloesterno in 1 2 3 4 5
do
echo -n "Gruppo $cicloesterno: "
#---------------------------------
for ciclointerno in 1 2 3 4 5
do
echo -n "$ciclointerno "
if [ "$ciclointerno" -eq 3 ]
then
break # Provate break 2 per vedere il risultato.
# ("Interrompe" entrambi i cicli, interno ed esterno).
fi
done
#---------------------------------
echo
done
echo
exit 0 |
Il comando continue, come
break, può avere un parametro.
Un semplice continue interrompe
l'esecuzione dell'iterazione corrente del ciclo e dà
inizio alla successiva. continue N
salta tutte le restanti iterazioni del ciclo in cui si
trova e continua con l'iterazione successiva del
ciclo N di livello superiore.
Esempio 10-22. Proseguire ad un livello di ciclo superiore
#!/bin/bash
# Il comando "continue N", continua all'Nsimo livello.
for esterno in I II III IV V # ciclo esterno
do
echo; echo -n "Gruppo $esterno: "
# ----------------------------------------------------
for interno in 1 2 3 4 5 6 7 8 9 10 # ciclo interno
do
if [ "$interno" -eq 7 ]
then
continue 2 # Continua al ciclo di 2 livello, cioè il
#+ "ciclo esterno". Modificate la riga precedente
#+ con un semplice "continue" per vedere il
#+ consueto comportamento del ciclo.
fi
echo -n "$interno " # 7 8 9 10 non verranno mai visualizzati.
done
# ----------------------------------------------------
done
echo; echo
# Esercizio:
# Trovate un valido uso di "continue N" in uno script.
exit 0 |
Esempio 10-23. Uso di continue N in un caso reale
# Albert Reiner fornisce un esempio di come usare "continue N":
# ---------------------------------------------------------
# Supponiamo di avere un numero elevato di job che devono essere
#+ eseguiti, con tutti i dati che devono essere trattati contenuti in un
#+ file, che ha un certo nome ed è inserito in una data directory.
#+ Ci sono diverse macchine che hanno accesso a questa directory e voglio
#+ distribuire il lavoro su tutte queste macchine. Per far questo,
#+ solitamente, utilizzo nohup con il codice seguente su ogni macchina:
while true
do
for n in .iso.*
do
[ "$n" = ".iso.opts" ] && continue
beta=${n#.iso.}
[ -r .Iso.$beta ] && continue
[ -r .lock.$beta ] && sleep 10 && continue
lockfile -r0 .lock.$beta || continue
echo -n "$beta: " `date`
run-isotherm $beta
date
ls -alF .Iso.$beta
[ -r .Iso.$beta ] && rm -f .lock.$beta
continue 2
done
break
done
# I dettagli, in particolare sleep N, sono specifici per la mia
#+ applicazione, ma la struttura generale è:
while true
do
for job in {modello}
do
{job già terminati o in esecuzione} && continue
{marca il job come in esecuzione, lo esegue, lo marca come eseguito}
continue 2
done
break # Oppure `sleep 600' per evitare la conclusione.
done
# In questo modo lo script si interromperà solo quando non ci saranno
#+ più job da eseguire (compresi i job che sono stati aggiunti durante
#+ il runtime). Tramite l'uso di appropriati lockfile può essere
#+ eseguito su diverse macchine concorrenti senza duplicazione di
#+ calcoli [che, nel mio caso, occupano un paio d'ore, quindi è
#+ veramente il caso di evitarlo]. Inoltre, poiché la ricerca
#+ ricomincia sempre dall'inizio, è possibile codificare le priorità
#+ nei nomi dei file. Naturalmente, questo si potrebbe fare senza
#+ `continue 2', ma allora si dovrebbe verificare effettivamente se
#+ alcuni job sono stati eseguiti (in questo caso dovremmo cercare
#+ immediatamente il job successivo) o meno (in quest'altro dovremmo
#+ interrompere o sospendere l'esecuzione per molto tempo prima di
#+ poter verificare un nuovo job). |
![]() | Il costrutto continue N è difficile da capire e complicato da usare, in modo significativo, in qualsiasi contesto. Sarebbe meglio evitarlo. |
| [1] | Sono builtin di shell, mentre altri comandi di ciclo, come while e case, sono parole chiave. |