Loop Through the Lines of a File: Bash For Loop Explained
Voglio fare un loop attraverso le linee di un file con uno script Bash e uno dei modi per farlo è usare un ciclo for.
Cos’è un ciclo for?
Un ciclo for è uno dei più comuni costrutti di programmazione ed è usato per eseguire un dato blocco di codice dato un insieme di elementi in una lista. Per esempio, diciamo che volete scrivere un programma che stampi il numero di persone che vivono nelle 10 più grandi città europee. Il programma può usare un ciclo for per passare attraverso ogni città nella lista e stampare il numero di persone per quella città.
La logica eseguita è ogni volta la stessa e l’unica cosa che cambia è la città.
Sotto potete vedere la sintassi generica per un ciclo Bash for:
for item in do command1 command2 ... commandNdone
LIST può essere, per esempio:
- un intervallo di numeri.
- una sequenza di stringhe separate da spazi.
- l’output di un comando Linux (ad esempio il comando ls).
Gli N comandi tra do e done vengono eseguiti per ogni elemento della lista.
For Loop in Bash
In questo articolo imparerai come usare il ciclo for in Bash e in particolare per passare attraverso le linee di un file.
Ma perché dovresti farlo? Passare attraverso le righe di un file?
Per esempio, potresti averne bisogno se hai esportato dati da un’applicazione in un file e vuoi elaborare quei dati in qualche modo.
In questo esempio useremo un semplice file .txt in cui ogni riga contiene:
- il nome di una città
- il numero di persone che vivono in quella città.
Sotto potete vedere il formato del file di testo, un due punti è usato per separare ogni città dal numero di persone che vivono in quella città:
Istanbul:15,067,724Moscow:12,615,279London:9,126,366...
Quindi, come possiamo usare un ciclo Bash for per passare attraverso il contenuto di questo file?
Prima memorizzeremo il nome del file in una variabile
FILENAME="european-cities.txt"
Dopo di che, useremo un’altra variabile e il comando cat per ottenere tutte le righe del file:
LINES=$(cat $FILENAME)
Qui stiamo usando la sostituzione del comando per assegnare l’output del comando cat alle variabili LINES.
Finalmente il ciclo for permette di passare attraverso ogni linea del file:
for LINE in $LINESdo echo "$LINE"done
Do e done sono usati per definire i comandi da eseguire ad ogni iterazione del ciclo for.
Per esempio, se avete un file con 10 linee il ciclo for passerà attraverso 10 iterazioni e ad ogni iterazione leggerà una linea del file.
Il comando echo può essere sostituito da qualsiasi sequenza di comandi in base a ciò che volete fare con ogni linea del file.
Ecco lo script finale:
#!/bin/bash FILENAME="european-cities.txt"LINES=$(cat $FILENAME)for LINE in $LINESdo echo "$LINE"done
E l’output dello script è…
./cities.sh Istanbul:15,067,724Moscow:12,615,279London:9,126,366Saint-Petersburg:5,383,890Berlin:3,748,148Kyiv:3,703,100Madrid:3,223,334Rome:2,857,321Paris:2,140,526Bucharest:2,106,144
Stiamo passando la lista al ciclo for usando il comando cat.
Questo significa che possiamo usare qualsiasi comando che vogliamo per generare la LISTA da passare al ciclo for.
Hai in mente altri possibili comandi?
Inoltre, il ciclo for non è l’unica opzione per creare un ciclo in uno script Bash, un’altra opzione è un ciclo while.
Cos’è un contatore in un ciclo for di Bash?
In un ciclo for si può anche definire una variabile chiamata contatore. Puoi usare un contatore per tenere traccia di ogni iterazione del ciclo.
L’uso di un contatore è molto comune in tutti i linguaggi di programmazione. Può anche essere usato per accedere agli elementi di una struttura dati all’interno del ciclo (questo non è il caso del nostro esempio).
Modifichiamo il programma precedente e definiamo un contatore il cui valore viene stampato ad ogni iterazione:
#!/bin/bash FILENAME="european-cities.txt"LINES=$(cat $FILENAME)COUNTER=0for LINE in $LINESdo echo "Counter $COUNTER: $LINE" COUNTER=$((COUNTER+1))done
Come potete vedere ho definito una variabile chiamata COUNTER fuori dal ciclo for con il suo valore iniziale impostato a 0.
Poi ad ogni iterazione stampo il valore del contatore insieme alla riga del file.
Dopo aver fatto questo uso l’operatore aritmetico Bash per aumentare il valore della variabile COUNTER di 1.
Ecco l’output dello script:
Counter 0: Istanbul:15,067,724Counter 1: Moscow:12,615,279Counter 2: London:9,126,366Counter 3: Saint-Petersburg:5,383,890Counter 4: Berlin:3,748,148Counter 5: Kyiv:3,703,100Counter 6: Madrid:3,223,334Counter 7: Rome:2,857,321Counter 8: Paris:2,140,526Counter 9: Bucharest:2,106,144
Break and Continue in a Bash For Loop
Ci sono modi per alterare il normale flusso di un ciclo for in Bash.
Le due istruzioni che permettono di farlo sono break e continue:
- break: interrompe l’esecuzione del ciclo for e salta alla prima riga dopo for loop.
- continue: salta alla prossima iterazione del ciclo for.
Aver definito un contatore ci aiuta a vedere cosa succede quando aggiungiamo break o continue al nostro script esistente.
Iniziamo con break…
Aggiungo un’istruzione if basata sul valore del contatore. L’istruzione break all’interno dell’if interrompe l’esecuzione del ciclo se il contatore è uguale a 3:
#!/bin/bash FILENAME="european-cities.txt"LINES=$(cat $FILENAME)COUNTER=0for LINE in $LINESdo if ; then break fi echo "Counter $COUNTER: $LINE" COUNTER=$((COUNTER+1))done
E l’output è:
Counter 0: Istanbul:15,067,724Counter 1: Moscow:12,615,279Counter 2: London:9,126,366
Come potete vedere l’istruzione break interrompe l’esecuzione del ciclo for prima di raggiungere il comando echo perché COUNTER è 3.
Dopo questo, sostituiamo break con continue e vediamo cosa succede. Lascerò il resto del codice invariato.
#!/bin/bash FILENAME="european-cities.txt"LINES=$(cat $FILENAME)COUNTER=0for LINE in $LINESdo if ; then continue fi echo "Counter $COUNTER: $LINE" COUNTER=$((COUNTER+1))done
Ed ecco l’output dello script:
Counter 0: Istanbul:15,067,724Counter 1: Moscow:12,615,279Counter 2: London:9,126,366
Strano…l’output è lo stesso. Perché?
Questo perché quando il valore di COUNTER è 3 l’istruzione continue salta alla prossima iterazione del ciclo ma non incrementa il valore del contatore.
Quindi alla prossima iterazione il valore di COUNTER è ancora 3 e l’istruzione continue viene eseguita di nuovo, e così via per tutte le altre iterazioni.
Per risolvere questo problema dobbiamo aumentare il valore della variabile COUNTER all’interno dell’istruzione if:
#!/bin/bash FILENAME="european-cities.txt"LINES=$(cat $FILENAME)COUNTER=0for LINE in $LINESdo if ; then COUNTER=$((COUNTER+1)) continue fi echo "Counter $COUNTER: $LINE" COUNTER=$((COUNTER+1))done
Questa volta vediamo l’output corretto:
Counter 0: Istanbul:15,067,724Counter 1: Moscow:12,615,279Counter 2: London:9,126,366Counter 4: Berlin:3,748,148Counter 5: Kyiv:3,703,100Counter 6: Madrid:3,223,334Counter 7: Rome:2,857,321Counter 8: Paris:2,140,526Counter 9: Bucharest:2,106,144
Come potete vedere “Counter 3: ….” non viene stampato nel terminale.
Scrivere un ciclo for in una riga
Prima di finire questo tutorial, vediamo come possiamo scrivere un ciclo for in una riga.
Questa non è una pratica consigliata considerando che rende il tuo codice meno leggibile.
Ma è bene sapere come scrivere un ciclo in una riga, dà più profondità alla tua conoscenza di Bash.
La sintassi generica per un ciclo Bash for in una riga è la seguente:
for i in ; do ; done
Stampiamo il contenuto del nostro file di testo con una riga for loop:
#!/bin/bashFILENAME="european-cities.txt"LINES=$(cat $FILENAME)for LINE in $LINES; do echo $LINE; done
Per semplificare le cose ho rimosso il COUNTER e l’istruzione if. Se ci fossero stati, la linea del ciclo for sarebbe stata molto più difficile da leggere.
Cercate di stare lontani dalle linee singole se rendono il vostro codice difficile da leggere.
Conclusione
In conclusione, in questo tutorial avete imparato come:
- Memorizzare le linee di un file in una variabile
- Utilizzare un ciclo for per esaminare ogni linea.
- Utilizzare un contatore in un ciclo for.
- Cambiare il flusso di un ciclo con break e continue.
- Scrivere un ciclo for in una riga.
Ha senso?
Come lo userai?
Fammi sapere 🙂
Se vuoi imparare di più sui cicli nello scripting Bash dai un’occhiata a questo tutorial.
Corso GRATUITO correlato: Decifrare lo scripting Bash