GNU/Linux >> Linux Esercitazione >  >> Linux

Itera sulle righe anziché sulle parole in un ciclo for dello script di shell

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

Linux
  1. Ssh:script di shell per l'accesso a un server Ssh?

  2. Come scorrere le righe di un file?

  3. 12 Esempi di Bash For Loop per gli script della tua shell Linux

  4. Come cercare i file usando regex nello script della shell linux

  5. Script di shell Linux per il backup del database

Bash For Loop

Comprendere il ciclo for negli script della shell

Script Bash per Loop spiegato con esempi

Come posso eseguire il loop sull'output di un comando di shell?

Shell Script while loop:[intorno a una pipeline manca `]'

Ciclo for nidificato