Ci

Cicli in Bash

{one: "1 esercizio", many: "%{count} esercizi", other: "%{count} esercizi"}

Informazioni su Cicli

Bash ha due forme di ciclo:

  • Ripetere alcuni comandi per ogni elemento di una lista di elementi,
  • Ripetere alcuni comandi mentre una condizione è vera (o falsa).

I cicli for

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.

Il ciclo for aritmetico

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

Quando non usare for

Non leggere le righe con for.

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.

  1. La sostituzione di comando non è racchiusa tra virgolette, quindi è soggetta alla divisione in parole. La divisione in parole, per impostazione predefinita, divide sugli spazi bianchi, non solo sulle nuove righe. Questo ciclo for sta iterando sulle parole del file, non sulle righe.
  2. La sostituzione di comando non è racchiusa tra virgolette, quindi è soggetta all'espansione dei nomi di file. Ogni parola del file verrà confrontata come pattern glob.

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.

I cicli 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».

Controllare i cicli

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.

Leggere le righe di un file

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.

Until

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

Cicli infiniti

A volte vuoi ripetere all'infinito. Ecco due modi per farlo.

  1. un ciclo while con una condizione che ha sempre uno stato di uscita di successo

    while true; do ...
    
  2. un ciclo for aritmetico con espressioni vuote

    for ((;;)); do ...
    

Do-while

Non esiste un costrutto do-while esplicito, ma puoi ottenere lo stesso effetto:

while true; do
    COMMANDS

    if END_CONDITION; then
        break
    fi
done
Modifica tramite GitHub Il collegamento si apre in una nuova finestra o scheda