Guida avanzata di scripting Bash: Un'approfondita esplorazione dell'arte dello scripting di shell | ||
---|---|---|
Indietro | Capitolo 19. Redirezione I/O | Avanti |
Anche i blocchi di codice, come i cicli while, until e for, nonché i costrutti di verifica if/then, possono prevedere la redirezione dello stdin. Persino una funzione può usare questa forma di redirezione (vedi Esempio 23-11). È l'operatore <, posto alla fine del blocco, che svolge questo compito.
Esempio 19-5. Ciclo while rediretto
#!/bin/bash # redir2.sh if [ -z "$1" ] then Nomefile=nomi.data # È il file predefinito, se non ne viene #+ specificato alcuno. else Nomefile=$1 fi #+ Nomefile=${1:-nomi.data} # può sostituire la verifica precedente (sostituzione di parametro). conto=0 echo while [ "$nome" != Smith ] # Perché la variabile $nome è stata usata #+ con il quoting? do read nome # Legge da $Nomefile invece che dallo stdin. echo $nome let "conto += 1" done <"$Nomefile" # Redirige lo stdin nel file $Nomefile. # ^^^^^^^^^^^^ echo; echo "$conto nomi letti"; echo exit 0 # È da notare che, in alcuni linguaggi di scripting di shell più vecchi, il #+ ciclo rediretto viene eseguito come una subshell. # Di conseguenza $conto restituirebbe 0, il valore di inizializzazione prima #+ del ciclo. # Bash e ksh evitano l'esecuzione di una subshell "ogni qual volta questo sia #+ possibile", cosicché questo script, ad esempio, funziona correttamente. # (Grazie a Heiner Steven per la precisazione.) # Tuttavia . . . # Bash, talvolta, *può* eseguire una subshell per un ciclo "while-read" #+ posto dopo una PIPE, diversamente da un ciclo "while" REDIRETTO. abc=hi echo -e "1\n2\n3" | while read l do abc="$l" echo $abc done echo $abc # Grazie a Bruno de Oliveira Schneider per averlo dimostrato #+ con il precedente frammento di codice. # Grazie anche a Brian Onn per la correzione di un errore di notazione. |
Esempio 19-6. Una forma alternativa di ciclo while rediretto
#!/bin/bash # Questa è una forma alternativa dello script precedente. # Suggerito da Heiner Steven #+ come espediente in quelle situazioni in cui un ciclo rediretto #+ viene eseguito come subshell e, quindi, le variabili all'interno del ciclo #+ non conservano i loro valori dopo che lo stesso è terminato. if [ -z "$1" ] then Nomefile=nomi.data # È il file predefinito, se non ne viene #+ specificato alcuno. else Nomefile=$1 fi exec 3<&0 # Salva lo stdin nel descrittore di file 3. exec 0<"$Nomefile" # Redirige lo standard input. conto=0 echo while [ "$nome" != Smith ] do read nome # Legge dallo stdin rediretto ($Nomefile). echo $nome let "conto += 1" done # Il ciclo legge dal file $Nomefile. #+ a seguito dell'istruzione alla riga 21. # La versione originaria di questo script terminava il ciclo "while" con #+ done <"$Nomefile" # Esercizio: # Perché questo non è più necessario? exec 0<&3 # Ripristina il precedente stdin. exec 3<&- # Chiude il temporaneo df 3. echo; echo "$conto nomi letti"; echo exit 0 |
Esempio 19-7. Ciclo until rediretto
#!/bin/bash # Uguale all'esempio precedente, ma con il ciclo "until". if [ -z "$1" ] then Nomefile=nomi.data # È il file predefinito, se non ne viene #+ specificato alcuno. else Nomefile=$1 fi # while [ "$nome" != Smith ] until [ "$nome" = Smith ] # Il != è cambiato in =. do read nome # Legge da $Nomefile, invece che dallo stdin. echo $nome done <"$Nomefile" # Redirige lo stdin nel file $Nomefile. # ^^^^^^^^^^^^ # Stessi risultati del ciclo "while" dell'esempio precedente. exit 0 |
Esempio 19-8. Ciclo for rediretto
#!/bin/bash if [ -z "$1" ] then Nomefile=nomi.data # È il file predefinito, se non ne viene #+ specificato alcuno. else Nomefile=$1 fi conta_righe=`wc $Nomefile | awk '{ print $1 }'` # Numero di righe del file indicato. # # Elaborato e con diversi espedienti, ciò nonostante mostra che #+ è possibile redirigere lo stdin in un ciclo "for" ... #+ se si è abbastanza abili. # # Più conciso conta_righe=$(wc -l < "$Nomefile") for nome in `seq $conta_righe` # Ricordo che "seq" genera una sequenza #+ di numeri. # while [ "$nome" != Smith ] -- più complicato di un ciclo "while" -- do read nome # Legge da $Nomefile, invece che dallo stdin. echo $nome if [ "$nome" = Smith ] # Sono necessarie tutte queste istruzioni #+ aggiuntive. then break fi done <"$Nomefile" # Redirige lo stdin nel file $Nomefile. # ^^^^^^^^^^^^ exit 0 |
Il precedente esempio può essere modificato per redirigere anche l'output del ciclo.
Esempio 19-9. Ciclo for rediretto (rediretti sia lo stdin che lo stdout)
#!/bin/bash if [ -z "$1" ] then Nomefile=nomi.data # È il file predefinito, se non ne viene #+ specificato alcuno. else Nomefile=$1 fi Filereg=$Nomefile.nuovo # Nome del file in cui vengono salvati i #+ risultati. NomeFinale=Jonah # Nome per terminare la "lettura". conta_righe=`wc $Nomefile | awk '{ print $1 }'` # Numero di righe del file #+ indicato. for nome in `seq $conta_righe` do read nome echo "$nome" if [ "$nome" = "$NomeFinale" ] then break fi done < "$Nomefile" > "$Filereg" # Redirige lo stdin nel file $Nomefile, # ^^^^^^^^^^^^^^^^^^^^^^^^^^ e lo salva nel file di backup $Filereg. exit 0 |
Esempio 19-10. Costrutto if/then rediretto
#!/bin/bash if [ -z "$1" ] then Nomefile=nomi.data # È il file predefinito, se non ne viene #+ specificato alcuno. else Nomefile=$1 fi TRUE=1 if [ "$TRUE" ] # vanno bene anche if true e if : then read nome echo $nome fi <"$Nomefile" # ^^^^^^^^^^^^ # Legge solo la prima riga del file. # Il costrutto "if/then" non possiede alcuna modalità di iterazione #+ se non inserendolo in un ciclo. exit 0 |
Esempio 19-11. File dati nomi.data usato negli esempi precedenti
Aristotile Belisario Capablanca Eulero Goethe Hamurabi Jonah Laplace Maroczy Purcell Schmidt Semmelweiss Smith Turing Venn Wilson Znosko-Borowski # Questo è il file dati per #+ "redir2.sh", "redir3.sh", "redir4.sh", "redir4a.sh", "redir5.sh". |
Redirigere lo stdout di un blocco di codice ha l'effetto di salvare il suo output in un file. Vedi Esempio 3-2.
Gli here document rappresentano casi particolari di blocchi di codice rediretti. Stando così le cose, è possibile redirigere l'output di un here document nello stdin per un ciclo while.
# Esempio fornito da Albert Siersema # Usato con il suo permesso (grazie!). function creaOutput() # Naturalmente, potrebbe anche essere un comando esterno. # Qui viene mostrato come si possa usare una funzione allo stesso modo. { ls -al *.jpg | awk '{print $5,$9}' } nr=0 # Vogliamo che il ciclo while sia in grado di manipolare queste dimensTotale=0 #+ variabili e visualizzare i cambiamenti al termine del ciclo. while read dimensFile Nomefile ; do echo "$Nomefile è grande $dimensFile byte" let nr++ dimensTotale=$((dimensTotale+dimensFile)) # O: "let dimensTotale+=dimensFile" done<<EOF $(creaOutput) EOF echo "$nr file per un totale di $totalSize byte" |