Die Falle ist das
IFS=; while read..
Legt die IFS
für die gesamte Shell-Umgebung außerhalb der Schleife fest, wohingegen
while IFS= read
Definiert es nur für den read
Aufruf 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.
while IFS=X read
teilt sich nicht umX
,while IFS=X; read
tut es aber ...while
nicht viel Sinn macht - die Bedingung für diewhile
Enden an diesem Semikolon, so gibt es keine eigentliche Schleife ...read
wird nur der erste Befehl in der einelementige Schleife ... Oder auch nicht ? Was ist mit demdo
dann ..?while
Bedingung haben (vorherdo
).IFS=
, aberIFS=X
nicht ... (oder vielleicht habe ich eine Weile darüber nachgedacht .. Kaffeepause benötigt :)Schauen wir uns ein Beispiel mit einem sorgfältig ausgearbeiteten Eingabetext an:
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
(aberprintf '%s\n' "$text"
um vorsichtig zu drucken,$text
ohne dass die Gefahr einer Erweiterung besteht). (Unten sehen Sie$
die Shell-Eingabeaufforderung.)read
aß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 wirread -r
.Das ist besser, wir haben wie erwartet zwei Zeilen. Die beiden Zeilen enthalten fast den gewünschten Inhalt: Der doppelte Abstand zwischen
hello
undworld
wurde beibehalten, da er sich innerhalb derline
Variablen befindet. Auf der anderen Seite wurde der anfängliche Platz aufgefressen. Das liegt daran, dassread
so 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
IFS
Variable auf einen leeren Wert.Beachten Sie, wie wir
IFS
speziell für die Dauer desread
eingebauten einstellen . DasIFS= read -r line
setzt die UmgebungsvariableIFS
(auf einen leeren Wert) speziell für die Ausführung vonread
. 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). Daread
es sich um eine integrierte Variable handelt, gelangt die Variable nie in die Umgebung eines externen Prozesses. nichtsdestotrotz ist der Wert von$IFS
das, was wir dort zuweisen, solangeread
es ausgeführt wird¹. Beachten Sie, dass diesread
keine spezielle integrierte Funktion ist , sodass die Zuweisung nur für ihre Dauer gültig ist.Daher achten wir darauf, den Wert von nicht
IFS
für andere Anweisungen zu ändern, die möglicherweise darauf angewiesen sind. Dieser Code funktioniert unabhängig von derIFS
ursprünglichen Einstellung des umgebenden Codes und verursacht keine Probleme, wenn der Code in der Schleife darauf angewiesen istIFS
.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.
Wenn die Schleife wäre
while IFS=; read -r name; do …
,for dir in $PATH
würde sie nicht$PATH
in durch Doppelpunkte getrennte Komponenten aufgeteilt. Wenn der CodeIFS=; while read …
wäre, wäre es noch offensichtlicher, dass im SchleifenkörperIFS
nicht festgelegt:
ist.Natürlich wäre es möglich, den Wert von
IFS
nach der Ausführung wiederherzustellenread
. Dafür müsste man jedoch den vorherigen Wert kennen, was ein zusätzlicher Aufwand ist.IFS= read
ist der einfache Weg (und bequemerweise auch der kürzeste Weg).¹ Und wenn
read
dies 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.quelle
while IFS= read
(ohne ein Semikolon danach=
) keine spezielle Form vonwhile
oder vonIFS
oder von istread
. Das Konstrukt ist generisch: dh.anyvar=anyvalue anycommand
. Das Fehlen von;
after settinganyvar
macht den Gültigkeitsbereich vonanyvar
local zuanycommand
. Die while-do / done-Schleife ist zu 100% unabhängig vom lokalen Gültigkeitsbereich vonany_var
.Abgesehen von der (bereits geklärt)
IFS
Scoping Unterschiede zwischen demwhile IFS='' read
,IFS=''; while read
undwhile IFS=''; read
Idiomen (pro-Befehl vs / script Shell weiterIFS
Variable 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
quelle
Inspiriert von Yuzems Antwort
Wenn Sie
IFS
einen tatsächlichen Charakter festlegen möchten , funktionierte dies für michquelle