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