GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Iterieren Sie in einer for-Schleife eines Shell-Skripts über Zeilen anstelle von Wörtern

Verwenden für

for l in $() führt eine Wortaufteilung basierend auf IFS durch:

$ 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 muss nicht zurückgesetzt werden, wenn es später nicht verwendet wird.

for l in $() führt auch eine Erweiterung des Pfadnamens durch:

$ 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
*

Wenn IFS=$'\n' , Zeilenvorschübe werden entfernt und reduziert:

$ 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) (oder $(<file.txt) ) liest auch die gesamte Datei in den Speicher.

Lesen verwenden

Ohne -r werden Backslashes zur Zeilenfortsetzung verwendet und vor anderen Zeichen entfernt:

$ 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

Zeichen in IFS werden am Zeilenanfang und -ende entfernt, aber nicht reduziert:

$ 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

Wenn die letzte Zeile nicht mit einem Zeilenumbruch endet, weist read ihr l zu, endet aber vor dem Schleifenkörper:

$ printf 'x\ny' | while read l; do echo $l; done
x
$ printf 'x\ny' | while read l || [[ $l ]]; do echo $l; done
x
y

Wenn sich eine While-Schleife in einer Pipeline befindet, befindet sie sich auch in einer Subshell, sodass Variablen außerhalb davon nicht sichtbar sind:

$ 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

Die for loop ist nicht dafür ausgelegt, "Linien" zu durchlaufen. Stattdessen wird "words" durchlaufen.

Kurze Terminologie:"Zeilen" sind Dinge, die durch Zeilenumbrüche getrennt sind. "Wörter" sind Dinge, die durch Leerzeichen (und unter anderem Zeilenumbrüche) getrennt sind. im Bash-Jargon werden "Wörter" "Felder" genannt.

Der idiomatische Weg, Zeilen zu durchlaufen, ist die Verwendung eines while Schleife in Kombination mit read .

ioscan -m dsf | while read -r line
do
  printf '%s\n' "$line"
done

Beachten Sie, dass sich die While-Schleife wegen der Pipe in einer Subshell befindet. Dies kann zu Verwirrung mit dem Gültigkeitsbereich von Variablen führen. In Bash können Sie dies umgehen, indem Sie die Prozesssubstitution verwenden.

while read -r line
do
  printf '%s\n' "$line"
done < <(ioscan -m dsf)

siehe auch http://mywiki.wooledge.org/BashFAQ/024

Die for-Schleife teilt die zu durchlaufenden Dinge unter Verwendung der Zeichen in $IFS auf Variable als Trennzeichen. IFS ist die Abkürzung für Internal Field Separator. Normalerweise $IFS enthält ein Leerzeichen, einen Tabulator und einen Zeilenumbruch. Das bedeutet die for Schleife wird die "Wörter" durchlaufen, nicht die Zeilen.

Wenn Sie darauf bestehen, eine for-Schleife zu verwenden, um Zeilen zu durchlaufen, müssen Sie den Wert von $IFS ändern nur Zeilenumbruch. Aber wenn Sie dies tun, müssen Sie den alten Wert von $IFS speichern und stellen Sie das nach der Schleife wieder her, da viele andere Dinge auch von $IFS abhängen .

OLDIFS="$IFS"
IFS=$'\n' # bash specific
for line in $(ioscan -m dsf)
do
  printf '%s\n' "$line"
done
IFS="$OLDIFS"

in POSIX-Shells, die kein ANSI-C Quoting haben ($'\n' ), kannst du es so machen:

IFS='
'

das heißt:Setzen Sie eine tatsächliche neue Zeile zwischen die Anführungszeichen.

Alternativ können Sie eine Subshell verwenden, um die Änderung zu $IFS einzuschließen :

(
  # 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

Aber Vorsicht, der Befehl in der Schleife kann selbst von einer vernünftigen Einstellung für $IFS abhängen . Dann müssen Sie den $IFS wiederherstellen bevor Sie den Befehl ausführen und vor der nächsten Schleife oder so etwas erneut setzen. Ich empfehle nicht, mit $IFS herumzuspielen . Zu viele Befehle hängen von vernünftigen Werten in $IFS ab und es zu ändern ist ein endloser Albtraum obskurer Fehlersuche.

Siehe auch:

  • 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 – Shell-Skript zum Einloggen in einen SSH-Server?

  2. Shell-Skript zum Verschieben der ältesten Dateien?

  3. Shell-Skript-Ausführungsrichtlinien für Neulinge

  4. Formatieren Sie Datum und Uhrzeit für Linux-Shell-Skript oder -Variable

  5. Bash-Skript:Verwendung eines Skriptbefehls aus einem Bash-Skript zum Protokollieren einer Sitzung

Bash für Schleife

Die for-Schleife in Shell-Skripten verstehen

Bash-Skript für Schleife mit Beispielen erklärt

Iterieren Sie mithilfe des Shell-Skripts über die $PATH-Variable

Shell-Skript While-Schleife:[um eine Pipeline herum fehlt `]'

Verschachtelte for-Schleife