Tutto ciò che devi sapere su Bash For Loops in Linux
Gli script Bash sono un mezzo altamente efficiente per automatizzare le attività, in particolare quelle che sfruttano altri programmi esistenti. Questa automazione spesso richiede di ripetere più volte un’operazione simile, che è precisamente il punto in cui entra in gioco il ciclo for .
Gli amministratori di sistema Linux e Mac hanno in genere familiarità con lo scripting tramite il terminale, ma anche gli utenti Windows possono entrare in azione con il sottosistema Windows per Linux .
Come funzionano gli script Bash
Uno script bash è semplicemente un semplice file di testo contenente una serie di comandi che la shell bash può leggere ed eseguire. Bash è la shell predefinita in macOS pre-Catalina e nella maggior parte delle distribuzioni Linux.
Se non hai mai lavorato con uno script di shell prima, dovresti iniziare con il caso più semplice in assoluto. Ciò ti consentirà di esercitarti sui concetti chiave, inclusa la creazione dello script e la sua esecuzione.
Innanzitutto, crea il seguente file in una posizione comoda (idealmente, apri un terminale e vai prima alla directory desiderata):
#!/bin/bash
echo "Hello, World"
La prima riga dice a qualunque cosa esegua questo programma come eseguirlo (cioè usando l’interprete bash). Il secondo è solo un comando come qualsiasi altro che potresti inserire nella riga di comando. Salva il file come hello_world.sh , quindi:
$ chmod +x hello_world.sh
$ ./hello_world.sh
Il comando chmod sulla prima riga rende il file eseguibile, il che significa che può essere eseguito digitandone il nome, come nella seconda riga.
Se vedi le parole “Hello, World” stampate su una riga nel tuo terminale, allora tutto funziona come richiesto.
Come funzionano i cicli For
Nella programmazione generale, ci sono due tipi principali di ciclo for: numerico e foreach . Il tipo numerico è tradizionalmente il più comune, ma nell’uso di bash, di solito è il contrario.
I cicli numerici per i cicli si concentrano tipicamente su un singolo intero che determina il numero di iterazioni che verranno eseguite, ad esempio:
for (i = 0; i < 100; i++) {
/* statements to execute repeatedly */
}
Questo è un ciclo dall’aspetto familiare che itererà esattamente 100 volte, a meno che i non venga alterato all’interno del ciclo, o un’altra istruzione interrompa l’esecuzione del ciclo for.
I cicli Foreach, al contrario, tendono a operare su strutture come elenchi o array e ripetono per ogni elemento all’interno di quella raccolta:
people = [ "Peter", "Paul", "Mary" ]
foreach (people as person) {
if (person == "Paul") {
...
}
}
Alcuni linguaggi utilizzano una sintassi leggermente diversa che scambia l’ordine di raccolta e oggetto:
people = [ "Peter", "Paul", "Mary" ]
for (person in people) {
if (person == "Paul") {
...
}
}
Per in Loops
In bash, il ciclo foreach, o for in, è più comune. La sintassi di base è, semplicemente:
for arg in [list]
do
/* statements to execute repeatedly */
/* the value of arg can be obtained using $arg */
done
Ad esempio, per scorrere tre file con nome esplicito:
for file in one.c two.c three.c
do
ls "$file"
done
Se tali file esistono nella directory corrente, l’output di questo script sarà:
one.c
two.c
three.c
Invece di un insieme fisso di file, l’elenco può essere ottenuto tramite un modello glob (uno che include i caratteri jolly – caratteri speciali che rappresentano altri caratteri). Nell’esempio seguente, il ciclo for esegue un’iterazione su tutti i file (nella directory corrente) i cui nomi terminano con “.xml”:
for file in *.xml
do
ls -l "$file"
done
Ecco alcuni output di esempio:
$ -rw-r--r-- 1 bobby staff 2436 3 Nov 2019 feed.xml
$ -rw-r--r-- 1 bobby staff 6447 27 Oct 16:24 sitemap.xml
Questo può sembrare un modo prolisso di fare:
$ ls -l *.xml
Ma c’è una differenza significativa: il ciclo for esegue il programma ls 2 volte separate, con un singolo nome di file passato ogni volta. Nell’esempio separato di ls, il modello glob (* .xml) corrisponde prima ai nomi dei file e poi li invia tutti, come singoli parametri della riga di comando, a un’istanza di ls .
Ecco un esempio che utilizza il programma wc (conteggio parole) per rendere la differenza più evidente:
$ wc -l *.xml
44 feed.xml
231 sitemap.xml
275 total
Il programma wc conta il numero di righe in ogni file separatamente, quindi stampa un conteggio totale su tutti loro. Al contrario, se wc opera all’interno di un ciclo for:
for file in *.xml
do
wc -l $file
done
Vedrai ancora il conteggio per ogni file:
44 feed.xml
231 sitemap.xml
Ma non esiste un totale di riepilogo generale perché wc viene eseguito in isolamento, ogni volta che il ciclo si ripete.
Quando una lista non è una lista
C’è un errore molto semplice e comune quando si ha a che fare con i cicli for, a causa del modo in cui bash gestisce gli argomenti / le stringhe tra virgolette. Il ciclo attraverso un elenco di file dovrebbe essere fatto in questo modo:
for file in one.c two.c
Non così:
for file in "one.c two.c"
Il secondo esempio racchiude i nomi dei file tra virgolette doppie che risulta in un elenco con un solo parametro; il ciclo for verrà eseguito solo una volta. Questo problema può essere evitato utilizzando una variabile in questi casi:
FILES="one.c two.c"
for file in $FILES
do
...
done
Nota che la dichiarazione della variabile stessa ha bisogno di racchiudere il suo valore tra virgolette doppie!
Per Senza un elenco
Senza nulla su cui eseguire l’iterazione, un ciclo for opera su qualsiasi argomento della riga di comando fornito allo script quando viene richiamato. Ad esempio, se hai uno script denominato args.sh contenente quanto segue:
#!/bin/sh
for a
do
echo $a
done
Quindi l’esecuzione di args.sh ti darà quanto segue:
$ ./args.sh one two three
one
two
three
Bash riconosce questo caso e considera a do l’equivalente di a in $ @ do dove $ @ è una variabile speciale che rappresenta gli argomenti della riga di comando.
Emulazione di un ciclo For numerico tradizionale
Gli script Bash spesso trattano elenchi di file o righe di output da altri comandi, quindi il tipo di ciclo for in è comune. Tuttavia, la tradizionale operazione in stile c è ancora supportata:
for (( i=1; i<=5; i++ ))
do
echo $i
done
Questa è la forma classica con tre parti in cui:
- una variabile viene inizializzata (i = 1) quando il ciclo viene incontrato per la prima volta
- il ciclo continua fintanto che la condizione (i <= 5) è vera
- ogni volta che si esegue il ciclo, la variabile viene incrementata (i ++)
L’iterazione tra due valori è un requisito abbastanza comune che esiste un’alternativa più breve e leggermente meno confusa:
for i in {1..5}
do
echo $i
done
L’espansione della parentesi graffa che si verifica traduce efficacemente il ciclo for di cui sopra in:
for i in 1 2 3 4
Controllo del loop più preciso con interruzione e continuazione
I cicli più complessi spesso richiedono un modo per uscire anticipatamente o riavviare immediatamente il ciclo principale con il valore successivo a turno. Per fare ciò, bash prende in prestito le istruzioni break e continue che sono comuni in altri linguaggi di programmazione. Ecco un esempio che utilizza entrambi per trovare il primo file lungo più di 100 caratteri:
#!/bin/bash
for file in *
do
if [ ! -f "$file" ]
then
echo "$file is not a file"
continue
fi
num_chars=$(wc -c < "$file")
echo $file is "$num_chars characters long"
if [ $num_chars -gt 100 ]
then
echo "Found $file"
break
fi
done
Il ciclo for qui opera su tutti i file nella directory corrente. Se il file non è un file normale (ad esempio se è una directory), l’istruzione continue viene utilizzata per riavviare il ciclo con il file successivo a sua volta. Se è un file regolare, il secondo blocco condizionale determinerà se contiene più di 100 caratteri. In tal caso, l’istruzione break viene utilizzata per lasciare immediatamente il ciclo for (e raggiungere la fine dello script).
Conclusione
Uno script bash è un file contenente una serie di istruzioni che possono essere eseguite. Un ciclo for consente di ripetere più volte parte di uno script. Con l’uso di variabili, comandi esterni e le istruzioni break e continue, gli script bash possono applicare una logica più complessa ed eseguire un’ampia gamma di attività.