Ogni amministratore di sistema ha probabilmente alcune abilità che ha appreso nel corso degli anni che può indicare e dire:"Questo ha cambiato il mio mondo". Quell'abilità, o quel bit di informazione, o quella tecnica hanno semplicemente cambiato il modo in cui faccio le cose. Per molti di noi, quella cosa è in loop in Bash. Esistono altri approcci all'automazione che sono sicuramente più robusti o scalabili. La maggior parte di essi non è paragonabile alla semplicità e alla pronta usabilità di for
loop, però.
Se vuoi automatizzare la configurazione di migliaia di sistemi, probabilmente dovresti usare Ansible. Tuttavia, se stai cercando di rinominare un migliaio di file o di eseguire lo stesso comando più volte, allora for
loop è sicuramente lo strumento giusto per il lavoro.
[ Potrebbe piacerti anche: Mastering loop con i modelli Jinja in Ansible ]
Se hai già un background di programmazione o di scripting, probabilmente hai familiarità con cosa for
i loop fanno. Se non lo sei, cercherò di scomporlo in un inglese semplice per te.
Il concetto di base è:PER un determinato insieme di elementi, FAI una cosa.
L'insieme di elementi specificato può essere un insieme letterale di oggetti o qualsiasi cosa che Bash possa estrapolare in un elenco. Ad esempio, il testo estratto da un file, l'output di un altro comando Bash o i parametri passati tramite la riga di comando. Anche convertire questa struttura di loop in uno script Bash è banale. In questo articolo, ti mostriamo alcuni esempi di come un for
loop può farti sembrare un eroe della riga di comando, quindi prendiamo alcuni di questi esempi e li inseriamo in uno script Bash più strutturato.
Struttura di base del ciclo for
Per prima cosa, parliamo della struttura di base di un for
loop, e poi entreremo in alcuni esempi.
La sintassi di base di un for
ciclo è:
for <variable name> in <a list of items>;do <some command> $<variable name>;done;
Il nome della variabile sarà la variabile che specifichi in do
sezione e conterrà l'elemento nel ciclo in cui ti trovi.
L'elenco di elementi può essere qualsiasi cosa che restituisca uno spazio o un elenco separato da una nuova riga.
Ecco un esempio:
$ for name in joey suzy bobby;do echo $name;done
È così semplice e non c'è molto da fare lì, ma ti fa iniziare. La variabile $nome conterrà l'elemento nell'elenco su cui sta attualmente operando il ciclo e una volta che il comando (o comandi) nel do
vengono eseguite, il ciclo si sposterà all'elemento successivo. Puoi anche eseguire più di un'azione per ciclo. Qualsiasi cosa tra do
e done
sarà eseguito. I nuovi comandi richiedono solo un ;
delimitandoli.
$ for name in joey suzy bobby; do echo first $name;echo second $name;done;
first joey
second joey
first suzy
second suzy
first bobby
second bobby
Ora per un po' di reale esempi.
Rinominare i file
Questo ciclo prende l'output del comando Bash ls *.pdf
ed esegue un'azione su ciascun nome file restituito. In questo caso, aggiungiamo la data odierna alla fine del nome del file (ma prima dell'estensione del file).
for i in $(ls *.pdf); do
mv $i $(basename $i .pdf)_$(date +%Y%m%d).pdf
done
Per illustrare, esegui questo ciclo in una directory contenente questi file:
file1.pdf
file2.pdf
...
fileN.pdf
I file verranno rinominati in questo modo:
file1_20210210.pdf
file2_20210210.pdf
...
fileN_20210210.pdf
In una directory con centinaia di file, questo ciclo consente di risparmiare una notevole quantità di tempo nel rinominarli tutti.
Estrarre elenchi di elementi
Immagina di avere un file che vuoi scp
a più server. Ricorda che puoi combinare il for
loop con altre funzionalità di Bash, come l'espansione della shell, che consente a Bash di espandere un elenco di elementi che si trovano in una serie. Questo può funzionare per lettere e numeri. Ad esempio:
$ echo {0..10}
0 1 2 3 4 5 6 7 8 9 10
Supponendo che i tuoi server siano denominati in una sorta di schema come web0 , web1 , web2 , web3 , puoi fare in modo che Bash ripeta la serie di numeri in questo modo:
$ for i in web{0..10};do scp somefile.txt ${i}:;done;
Questo scorrerà attraverso web0 , web1 , web2 , web3 e così via, eseguendo il comando su ogni elemento.
Puoi anche definire alcune iterazioni. Ad esempio:
$ for i in web{0..10} db{0..2} balance_{a..c};do echo $i;done
web0
web1
web2
web3
web4
web5
web6
web7
web8
web9
web10
db0
db1
db2
balance_a
balance_b
balance_c
Puoi anche combinare le iterazioni. Immagina di avere due data center, uno negli Stati Uniti, un altro in Canada, e la convenzione di denominazione del server identifica in quale data center viveva una coppia di server HA. Ad esempio, web-us-0 sarebbe il primo server web nel data center degli Stati Uniti, mentre web-ca-0 sarebbero web 0 controparte nel data center CA. Per eseguire qualcosa su entrambi i sistemi, puoi usare una sequenza come questa:
$ for i in web-{us,ca}-{0..3};do echo $i;done
web-us-0
web-us-1
web-us-2
web-us-3
web-ca-0
web-ca-1
web-ca-2
web-ca-3
Nel caso in cui i nomi dei tuoi server non siano facili da scorrere, puoi fornire un elenco di nomi a for
ciclo:
$ cat somelist
first_item
middle_things
foo
bar
baz
last_item
$ for i in `cat somelist`;do echo "ITEM: $i";done
ITEM: first_item
ITEM: middle_things
ITEM: foo
ITEM: bar
ITEM: baz
ITEM: last_item
Nidificazione
Puoi anche combinare alcune di queste idee per casi d'uso più complessi. Ad esempio, immagina di voler copiare un elenco di file sui tuoi server web che seguono la convenzione di denominazione numerata che hai utilizzato nell'esempio precedente.
Puoi farlo iterando un secondo elenco basato sul tuo primo elenco attraverso cicli nidificati. Questo diventa un po' difficile da seguire quando lo fai come una battuta, ma può sicuramente essere fatto. Il tuo for
annidato loop viene eseguito ad ogni iterazione del genitore for
ciclo continuo. Assicurati di specificare nomi di variabili diversi per ogni ciclo.
Per copiare l'elenco dei file file1.txt, file2.txt, e file3.txt ai server web, usa questo ciclo annidato:
$ for i in file{1..3};do for x in web{0..3};do echo "Copying $i to server $x"; scp $i $x; done; done
Copying file1 to server web0
Copying file1 to server web1
Copying file1 to server web2
Copying file1 to server web3
Copying file2 to server web0
Copying file2 to server web1
Copying file2 to server web2
Copying file2 to server web3
Copying file3 to server web0
Copying file3 to server web1
Copying file3 to server web2
Copying file3 to server web3
Rinomina più creatività
Potrebbero esserci altri modi per farlo, ma ricorda, questo è solo un esempio di cose che puoi fare con un for
ciclo continuo. Che cosa succede se hai una montagna di file con un nome simile a FILE002.txt e vuoi sostituire FILE con qualcosa come TESTO . Ricorda che oltre a Bash stesso, hai anche altri strumenti open source a tua disposizione, come sed
, grep
, e altro ancora. Puoi combinare questi strumenti con il for
loop, in questo modo:
$ ls FILE*.txt
FILE0.txt FILE10.txt FILE1.txt FILE2.txt FILE3.txt FILE4.txt FILE5.txt FILE6.txt FILE7.txt FILE8.txt FILE9.txt
$ for i in $(ls FILE*.txt);do mv $i `echo $i | sed s/FILE/TEXT/`;done
$ ls FILE*.txt
ls: cannot access 'FILE*.txt': No such file or directory
$ ls TEXT*.txt
TEXT0.txt TEXT10.txt TEXT1.txt TEXT2.txt TEXT3.txt TEXT4.txt TEXT5.txt TEXT6.txt TEXT7.txt TEXT8.txt TEXT9.txt
Aggiunta di un ciclo for a uno script Bash
Esecuzione di for
i loop direttamente sulla riga di comando sono fantastici e ti fanno risparmiare una notevole quantità di tempo per alcune attività. Inoltre, puoi includere for
loop come parte dei tuoi script Bash per maggiore potenza, leggibilità e flessibilità.
Ad esempio, puoi aggiungere l'esempio di ciclo nidificato a uno script Bash per migliorarne la leggibilità, in questo modo:
$ vim copy_web_files.sh
# !/bin/bash
for i in file{1..3};do
for x in web{0..3};do
echo "Copying $i to server $x"
scp $i $x
done
done
Quando salvi ed esegui questo script, il risultato è lo stesso dell'esecuzione dell'esempio di ciclo nidificato sopra, ma è più leggibile, inoltre è più facile da modificare e mantenere.
$ bash copy_web_files.sh
Copying file1 to server web0
Copying file1 to server web1
... TRUNCATED ...
Copying file3 to server web3
Puoi anche aumentare la flessibilità e la riutilizzabilità del tuo for
loop includendoli negli script Bash che consentono l'input di parametri. Ad esempio, per rinominare file come nell'esempio Rinomina più creatività sopra consentendo all'utente di specificare il suffisso del nome, utilizzare questo script:
$ vim rename_files.sh
# !/bin/bash
source_prefix=$1
suffix=$2
destination_prefix=$3
for i in $(ls ${source_prefix}*.${suffix});do
mv $i $(echo $i | sed s/${source_prefix}/${destination_prefix}/)
done
In questo script, l'utente fornisce il prefisso del file di origine come primo parametro, il suffisso del file come secondo e il nuovo prefisso come terzo parametro. Ad esempio, per rinominare tutti i file che iniziano con FILE , di tipo .txt a TESTO , esegui lo script in questo modo:
$ ls FILE*.txt
FILE0.txt FILE10.txt FILE1.txt FILE2.txt FILE3.txt FILE4.txt FILE5.txt FILE6.txt FILE7.txt FILE8.txt FILE9.txt
$ bash rename_files.sh FILE txt TEXT
$ ls TEXT*.txt
TEXT0.txt TEXT10.txt TEXT1.txt TEXT2.txt TEXT3.txt TEXT4.txt TEXT5.txt TEXT6.txt TEXT7.txt TEXT8.txt TEXT9.txt
È simile all'esempio originale, ma ora gli utenti possono specificare altri parametri per modificare il comportamento dello script. Ad esempio, per rinominare tutti i file che ora iniziano con TEXT a NUOVO , usa quanto segue:
$ bash rename_files.sh TEXT txt NEW
$ ls NEW*.txt
NEW0.txt NEW10.txt NEW1.txt NEW2.txt NEW3.txt NEW4.txt NEW5.txt NEW6.txt NEW7.txt NEW8.txt NEW9.txt
[ Un corso gratuito per te:Panoramica tecnica su virtualizzazione e migrazione dell'infrastruttura. ]
Conclusione
Si spera che questi esempi abbiano dimostrato la potenza di un for
loop alla riga di comando di Bash. Puoi davvero risparmiare molto tempo ed eseguire attività in un modo meno soggetto a errori con i loop. Solo stai attento. I tuoi loop faranno ciò che chiedi loro, anche se chiedi loro di fare qualcosa di distruttivo per sbaglio, come creare (o eliminare) volumi logici o dischi virtuali.
Ci auguriamo che Bash for
i loop cambiano il tuo mondo nello stesso modo in cui hanno cambiato il nostro.