Antworten:
Die Falle ist das
IFS=; while read..
Legt die IFSfür die gesamte Shell-Umgebung außerhalb der Schleife fest, wohingegen
while IFS= read
Definiert es nur für den readAufruf neu (außer in der Bourne-Shell). Sie können das überprüfen, indem Sie eine Schleife wie
while IFS= read xxx; ... done
Dann wird nach einer solchen Schleife echo "blabalbla $IFS ooooooo"gedruckt
blabalbla
ooooooo
während nach
IFS=; read xxx; ... done
die IFS bleibt neu definiert: jetzt echo "blabalbla $IFS ooooooo"druckt
blabalbla ooooooo
Also , wenn Sie das zweite Formular zu verwenden, müssen Sie zurücksetzen erinnern: IFS=$' \t\n'.
Der zweite Teil dieser Frage wurde hier zusammengeführt , daher habe ich die zugehörige Antwort hier entfernt.
whilenicht viel Sinn macht - die Bedingung für die while Enden an diesem Semikolon, so gibt es keine eigentliche Schleife ... readwird nur der erste Befehl in der einelementige Schleife ... Oder auch nicht ? Was ist mit dem dodann ..?
whileBedingung haben (vorher do).
IFS=, aber IFS=Xnicht ... (oder vielleicht habe ich eine Weile darüber nachgedacht .. Kaffeepause benötigt :)
Schauen wir uns ein Beispiel mit einem sorgfältig ausgearbeiteten Eingabetext an:
text=' hello world\
foo\bar'
Das sind zwei Zeilen, wobei die erste mit einem Leerzeichen beginnt und mit einem Backslash endet. Schauen wir uns zunächst an, was passiert, ohne dass Vorsichtsmaßnahmen getroffen werden müssen read(aber printf '%s\n' "$text"um vorsichtig zu drucken, $textohne dass die Gefahr einer Erweiterung besteht). (Unten sehen Sie $ die Shell-Eingabeaufforderung.)
$ printf '%s\n' "$text" |
while read line; do printf '%s\n' "[$line]"; done
[hello worldfoobar]
readaßen die Backslashes auf: backslash-newline bewirkt, dass die Newline ignoriert wird, und backslash-anything ignoriert diesen ersten Backslash. Um zu vermeiden, dass Backslashes speziell behandelt werden, verwenden wir read -r.
$ printf '%s\n' "$text" |
while read -r line; do printf '%s\n' "[$line]"; done
[hello world\]
[foo\bar]
Das ist besser, wir haben wie erwartet zwei Zeilen. Die beiden Zeilen enthalten fast den gewünschten Inhalt: Der doppelte Abstand zwischen hellound worldwurde beibehalten, da er sich innerhalb der lineVariablen befindet. Auf der anderen Seite wurde der anfängliche Platz aufgefressen. Das liegt daran, dass readso viele Wörter gelesen werden, wie Sie Variablen übergeben, mit der Ausnahme, dass die letzte Variable den Rest der Zeile enthält - aber sie beginnt immer noch mit dem ersten Wort, dh die anfänglichen Leerzeichen werden verworfen.
Um also jede Zeile buchstäblich lesen zu können, müssen wir sicherstellen, dass keine Wortspaltung stattfindet . Dazu setzen wir die IFSVariable auf einen leeren Wert.
$ printf '%s\n' "$text" |
while IFS= read -r line; do printf '%s\n' "[$line]"; done
[ hello world\]
[foo\bar]
Beachten Sie, wie wir IFS speziell für die Dauer des readeingebauten einstellen . Das IFS= read -r linesetzt die Umgebungsvariable IFS(auf einen leeren Wert) speziell für die Ausführung von read. Dies ist eine Instanz der allgemeinen einfachen Befehlssyntax : Eine (möglicherweise leere) Folge von Variablenzuweisungen, gefolgt von einem Befehlsnamen und seinen Argumenten (Sie können auch an jeder Stelle Umleitungen einfügen). Da reades sich um eine integrierte Variable handelt, gelangt die Variable nie in die Umgebung eines externen Prozesses. nichtsdestotrotz ist der Wert von $IFSdas, was wir dort zuweisen, solange reades ausgeführt wird¹. Beachten Sie, dass dies readkeine spezielle integrierte Funktion ist , sodass die Zuweisung nur für ihre Dauer gültig ist.
Daher achten wir darauf, den Wert von nicht IFSfür andere Anweisungen zu ändern, die möglicherweise darauf angewiesen sind. Dieser Code funktioniert unabhängig von der IFSursprünglichen Einstellung des umgebenden Codes und verursacht keine Probleme, wenn der Code in der Schleife darauf angewiesen ist IFS.
Vergleichen Sie diesen Codeausschnitt, der Dateien in einem durch Doppelpunkte getrennten Pfad nachschlägt. Die Liste der Dateinamen wird aus einer Datei gelesen, ein Dateiname pro Zeile.
IFS=":"; set -f
while IFS= read -r name; do
for dir in $PATH; do
## At this point, "$IFS" is still ":"
if [ -e "$dir/$name" ]; then echo "$dir/$name"; fi
done
done <filenames.txt
Wenn die Schleife wäre while IFS=; read -r name; do …, for dir in $PATHwürde sie nicht $PATHin durch Doppelpunkte getrennte Komponenten aufgeteilt. Wenn der Code IFS=; while read …wäre, wäre es noch offensichtlicher, dass im Schleifenkörper IFSnicht festgelegt :ist.
Natürlich wäre es möglich, den Wert von IFSnach der Ausführung wiederherzustellen read. Dafür müsste man jedoch den vorherigen Wert kennen, was ein zusätzlicher Aufwand ist. IFS= readist der einfache Weg (und bequemerweise auch der kürzeste Weg).
¹ Und wenn readdies durch ein überfülltes Signal unterbrochen wird, möglicherweise während die Überfüllung ausgeführt wird - dies wird von POSIX nicht angegeben und hängt von der Shell in der Praxis ab.
while IFS= read(ohne ein Semikolon danach =) keine spezielle Form von whileoder von IFSoder von ist read. Das Konstrukt ist generisch: dh. anyvar=anyvalue anycommand. Das Fehlen von ;after setting anyvarmacht den Gültigkeitsbereich von anyvar local zu anycommand. Die while-do / done-Schleife ist zu 100% unabhängig vom lokalen Gültigkeitsbereich von any_var.
Abgesehen von der (bereits geklärt) IFSScoping Unterschiede zwischen dem while IFS='' read, IFS=''; while readund while IFS=''; readIdiomen (pro-Befehl vs / script Shell weiter IFSVariable Scoping), die Take-Home - Lehre ist , dass Sie die führenden verlieren und Leerzeichen am Ende einer Eingabezeile , wenn das IFS - Variable ist auf (enthält ein) Leerzeichen gesetzt.
Dies kann schwerwiegende Folgen haben, wenn Dateipfade verarbeitet werden.
Daher ist das Setzen der IFS-Variablen auf die leere Zeichenfolge alles andere als eine schlechte Idee, da hierdurch sichergestellt wird, dass das führende und nachfolgende Leerzeichen einer Zeile nicht entfernt wird.
Siehe auch: Bash, Zeile für Zeile aus Datei lesen, mit IFS
(
shopt -s nullglob
touch ' file with spaces '
IFS=$' \t\n' read -r file <<<"$(printf '%s' *file*with*spaces*)"
ls -l "$file"
IFS='' read -r file <<<"$(printf '%s' *file*with*spaces*)"
ls -l "$file"
)
Inspiriert von Yuzems Antwort
Wenn Sie IFSeinen tatsächlichen Charakter festlegen möchten , funktionierte dies für mich
iconv -f cp1252 zapni.tv.php | while IFS='#' read -d'#' line
do
echo "$line"
done
while IFS=X readteilt sich nicht umX,while IFS=X; readtut es aber ...