Uso per
for l in $()
esegue la suddivisione delle parole in base a IFS:
$ for l in $(printf %b 'a b\nc'); do echo "$l"; done
a
b
c
$ IFS=$'\n'; for l in $(printf %b 'a b\nc'); do echo "$l"; done
a b
c
IFS non deve essere reimpostato se non viene utilizzato in seguito.
for l in $()
esegue anche l'espansione del percorso:
$ printf %b 'a\n*\n' > file.txt
$ IFS=$'\n'
$ for l in $(<file.txt); do echo "$l"; done
a
file.txt
$ set -f; for l in $(<file.txt); do echo "$l"; done; set +f
a
*
Se IFS=$'\n'
, gli avanzamenti riga vengono rimossi e compressi:
$ printf %b '\n\na\n\nb\n\n' > file.txt
$ IFS=$'\n'; for l in $(<file.txt); do echo "$l"; done
a
b
$(cat file.txt)
(o $(<file.txt)
) legge anche l'intero file in memoria.
Utilizzo della lettura
Senza -r le barre rovesciate vengono utilizzate per la continuazione della riga e rimosse prima di altri caratteri:
$ cat file.txt
\1\\2\
3
$ cat file.txt | while read l; do echo "$l"; done
1\23
$ cat file.txt | while read -r l; do echo "$l"; done
\1\\2\
3
I caratteri in IFS vengono rimossi dall'inizio e dalla fine delle righe ma non compressi:
$ printf %b '1 2 \n\t3\n' | while read -r l; do echo "$l"; done
1 2
3
$ printf %b ' 1 2 \n\t3\n' | while IFS= read -r l; do echo "$l"; done
1 2
3
Se l'ultima riga non termina con una nuova riga, read le assegna l ma esce prima del corpo del ciclo:
$ printf 'x\ny' | while read l; do echo $l; done
x
$ printf 'x\ny' | while read l || [[ $l ]]; do echo $l; done
x
y
Se un ciclo while si trova in una pipeline, si trova anche in una subshell, quindi le variabili non sono visibili al di fuori di essa:
$ x=0; seq 3 | while read l; do let x+=l; done; echo $x
0
$ x=0; while read l; do let x+=l; done < <(seq 3); echo $x
6
$ x=0; x=8 | x=9; echo $x
0
Il for
loop non è progettato per eseguire il loop su "linee". Al contrario, scorre su "parole".
Terminologia breve:le "linee" sono cose separate da newline. le "parole" sono cose separate da spazi (e newline, tra gli altri). nel gergo bash le "parole" sono chiamate "campi".
Il modo idiomatico per scorrere le righe è usare un while
loop in combinazione con read
.
ioscan -m dsf | while read -r line
do
printf '%s\n' "$line"
done
Si noti che il ciclo while è in una subshell a causa della pipe. Ciò può causare confusione con l'ambito variabile. In bash puoi aggirare questo problema usando la sostituzione del processo.
while read -r line
do
printf '%s\n' "$line"
done < <(ioscan -m dsf)
vedi anche http://mywiki.wooledge.org/BashFAQ/024
Il ciclo for divide le cose su cui eseguire il ciclo usando i caratteri nel $IFS
variabili come separatori. IFS è l'abbreviazione di Internal Field Separator. Di solito $IFS
contiene uno spazio, una tabulazione e una nuova riga. Ciò significa for
loop eseguirà il loop sulle "parole", non sulle righe.
Se insisti nell'usare un ciclo for per scorrere le righe devi cambiare il valore di $IFS
a solo newline. Ma se lo fai devi salvare il vecchio valore di $IFS
e ripristinalo dopo il ciclo, perché anche molte altre cose dipendono da $IFS
.
OLDIFS="$IFS"
IFS=$'\n' # bash specific
for line in $(ioscan -m dsf)
do
printf '%s\n' "$line"
done
IFS="$OLDIFS"
nelle shell POSIX, che non hanno ANSI-C Quoting ($'\n'
), puoi farlo in questo modo:
IFS='
'
ovvero:inserisci una nuova riga tra le virgolette.
In alternativa puoi usare una subshell per contenere la modifica a $IFS
:
(
# changes to variables in the subshell stay in the subshell
IFS=$'\n'
for line in $(ioscan -m dsf)
do
printf '%s\n' "$line"
done
)
# $IFS is not changed outside of the subshell
Ma attenzione, il comando nel ciclo potrebbe esso stesso dipendere da qualche impostazione sensata per $IFS
. Quindi devi ripristinare il $IFS
prima di eseguire il comando e impostare nuovamente prima del ciclo successivo o qualcosa del genere. Non consiglio di scherzare con $IFS
. Troppi comandi dipendono da alcuni valori sensati in $IFS
e cambiarlo è un incubo senza fine di oscure caccia ai bug.
Vedi anche:
- http://wiki.bash-hackers.org/syntax/ccmd/classic_for
- http://wiki.bash-hackers.org/commands/builtin/read
- http://mywiki.wooledge.org/IFS
- http://mywiki.wooledge.org/SubShell
- http://mywiki.wooledge.org/ProcessSubstitution