Bash ha due forme di ciclo:
Per iterare su una lista di elementi, usiamo il ciclo for.
for varname in words; do COMMANDS; done
words viene espanso usando la divisione in parole e l'espansione dei nomi di file (che abbiamo visto nel concetto di Quoting).
Ogni parola viene assegnata a turno alla variabile varname, e i COMMANDS vengono eseguiti.
Ecco alcuni esempi che mostrano quando il ciclo for può essere utile
Eseguire alcuni comandi su un gruppo di file.
# source all the bash library files in the current directory
for file in ./*.bash; do
source "$file"
done
Eseguire un comando su ciascuno dei parametri di uno script o di una funzione.
for arg in "$@"; do
echo "argument: ${arg}"
done
Eseguire un comando per ogni parola separata da spazi nell'output di un comando.
for i in $(seq 10); do
echo "${i} squared is $((i * i))"
done
Vedremo più avanti alcuni avvertimenti su questo stile.
Bash ha anche un ciclo aritmetico. Sarà in parte familiare a chi conosce altri linguaggi di programmazione.
for ((INITIALIZATION; CONDITION; INCREMENT)); do COMMANDS; done
Le doppie parentesi tonde in bash rappresentano un contesto aritmetico (vedremo più in dettaglio l'aritmetica di bash in un concetto successivo).
L'esempio precedente che usa seq si può scrivere così
for ((i = 1; i <= 10; i++)); do
echo "${i} squared is $((i * i))"
done
Questo è un anti-pattern che vedrai spesso: iterare sull'output di cat.
for line in $(cat some.file); do
do_something_with "$line"
done
È sbagliato per due motivi.
Come abbiamo visto nel concetto di Quoting, la divisione in parole può essere controllata con la variabile IFS, e l'espansione dei nomi di file può essere disattivata.
Ma questa tende a essere una soluzione fragile.
for è il metodo sbagliato per iterare sulle righe di un file.
Il modo idiomatico per leggere un file è con un ciclo while.
Usa while per ripetere una sequenza di comandi mentre una condizione è vera.
while CONDITION_COMMANDS; do COMMANDS; done
Come con if (come abbiamo visto nel concetto di Conditionals), non esiste una sintassi speciale per CONDITION_COMMANDS.
Lo stato di uscita della lista di comandi determinerà se il risultato è «vero» o «falso».
Il comando break esce dal ciclo.
Il controllo riprende con il comando che segue il terminatore done del ciclo.
Il comando continue salta all'iterazione successiva del ciclo.
Come accennato, un ciclo while è il modo idiomatico per leggere un file.
while IFS= read -r line; do
do_something_with "$line"
done < some.file
Il contenuto del file viene fornito come input al ciclo con la ridirezione <.
Il comando read restituisce «vero» se riesce a leggere una riga intera dal suo input.
Quando l'input è esaurito, oppure se l'ultima riga termina senza un newline finale, allora read restituisce «falso».
Il modo davvero idiomatico per leggere le righe di un file, anche se l'ultima riga non termina con un newline, è
while IFS= read -r line || [[ -n "$line" ]]; do ...
L'opzione -r dice a read di non sostituire le sequenze con barra rovesciata: una barra rovesciata è solo un carattere normale.
IFS= assegna la stringa vuota a IFS solo per la durata del comando read.
Questo disattiva temporaneamente la divisione in parole, così che eventuali spazi bianchi iniziali o finali nella riga di testo in arrivo vengano conservati.
Il costrutto until ripete una sequenza di comandi finché una condizione è falsa.
Il ciclo si ripete finché la condizione non diventa vera.
until CONDITION_COMMANDS; do COMMANDS; done
Raramente si usa until.
È più comune usare un ciclo «while not».
while ! CONDITION_COMANDS; do ...
A volte vuoi ripetere all'infinito. Ecco due modi per farlo.
un ciclo while con una condizione che ha sempre uno stato di uscita di successo
while true; do ...
un ciclo for aritmetico con espressioni vuote
for ((;;)); do ...
Non esiste un costrutto do-while esplicito, ma puoi ottenere lo stesso effetto:
while true; do
COMMANDS
if END_CONDITION; then
break
fi
done