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