Die folgenden Themen auf dieser Website und StackOverflow waren hilfreich, um die Funktionsweise zu verstehen IFS
:
- Was ist IFS im Kontext von for looping?
- So durchlaufen Sie die Zeilen einer Datei
- Bash, Zeile für Zeile aus Datei lesen, mit IFS
Ich habe aber noch ein paar kurze Fragen. Ich habe mich entschlossen, sie im selben Beitrag zu fragen, da ich denke, dass dies den zukünftigen Lesern helfen kann:
Q1. IFS
wird typischerweise im Zusammenhang mit "Feldaufteilung" diskutiert. Ist Feldaufteilung dasselbe wie Wortaufteilung ?
F2: Die POSIX-Spezifikation besagt :
Wenn der Wert von IFS null ist, wird keine Feldaufteilung durchgeführt.
Entspricht das Setzen IFS=
dem Setzen IFS
auf Null? Ist es das, was damit gemeint ist, es auch empty string
zu setzen?
F3: In der POSIX-Spezifikation habe ich Folgendes gelesen :
Wenn IFS nicht gesetzt ist, soll sich die Shell so verhalten, als ob der Wert von IFS ist
<space>, <tab> and <newline>
Angenommen, ich möchte den Standardwert von wiederherstellen IFS
. Wie mache ich das? (Genauer gesagt, wie verweise ich auf <tab>
und <newline>
?)
F4: Wie würde dieser Code abschließend lauten :
while IFS= read -r line
do
echo $line
done < /path_to_text_file
benimm dich, wenn wir die erste Zeile auf ändern
while read -r line # Use the default IFS value
oder zu:
while IFS=' ' read -r line
IFS
und ein nicht gesetztesIFS
sind sehr unterschiedlich. Die Antwort auf Q4 ist zum Teil falsch: Hier werden innere Trennzeichen nicht berührt, sondern nur führende und nachfolgende.IFS
, alle bedeutenIFS=
.IFS=' ' ; foo=( bar baz qux ) ; echo "${#foo[@]}"
. (Äh, was? Es sollten mehrere Leerzeichen enthalten sein, damit die Engine sie weiterhin entfernt).read
; Die letzte Variable nimmt alles auf, was übrig bleibt, mit Ausnahme des letzten Trennzeichens, und lässt innere Trennzeichen im Inneren.Frage 1: Ja. "Field Splitting" und "Word Splitting" sind zwei Begriffe für dasselbe Konzept.
Frage 2: Ja. Wenn
IFS
nicht gesetzt (dh nachunset IFS
), ist dies gleichbedeutendIFS
mit$' \t\n'
(ein Leerzeichen, ein Tabulator und eine neue Zeile). WennIFS
auf einen leeren Wert gesetzt ist (das ist, was "null" hier bedeutet) (dh nachIFS=
oderIFS=''
oderIFS=""
), wird überhaupt keine Feldaufteilung durchgeführt (und$*
bei der normalerweise das erste Zeichen von verwendet wird$IFS
, wird ein Leerzeichen verwendet).F3: Wenn Sie das Standardverhalten haben möchten
IFS
, können Sie verwendenunset IFS
. Wenn SieIFS
diesen Standardwert explizit festlegen möchten , können Sie das Literalzeichen Leerzeichen, Tabulatorzeichen und Zeilenumbruch in einfache Anführungszeichen setzen. In ksh93, bash oder zsh können Sie verwendenIFS=$' \t\n'
. Portabel, wenn Sie vermeiden möchten, dass Ihre Quelldatei ein literales Tabulatorzeichen enthält, können Sie verwendenQ4: Mit
IFS
Satz auf einen leeren Wert,read -r line
setztline
auf die ganze Linie mit Ausnahme seiner abschließenden Newline. MitIFS=" "
werden Leerzeichen am Anfang und am Ende der Zeile abgeschnitten. Mit dem Standardwert vonIFS
werden Tabulatoren und Leerzeichen abgeschnitten.quelle
$@
gibt es einige Variationen zwischen Shells in nicht aufgelisteten Kontexten wieIFS=; var=$@
). Es sollte beachtet werden, dass wenn IFS leer ist, keine Wortteilung durchgeführt wird, aber $ var immer noch zu keinem Argument erweitert wird, anstatt zu einem leeren Argument, wenn $ var leer ist, und Globbing weiterhin angewendet wird, so dass Sie weiterhin Variablen zitieren müssen (auch wenn Sie Globbing deaktivieren)Q1. Feldaufteilung.
Ja, beide weisen auf die gleiche Idee hin.
F2: Wann ist IFS null ?
Ja, alle drei bedeuten dasselbe: Es soll keine Feld- / Wortteilung durchgeführt werden. Dies wirkt sich auch auf das Drucken von Feldern aus (wie bei
echo "$*"
). Alle Felder werden ohne Leerzeichen zusammen verkettet.F3: (Teil a) Deaktivieren Sie IFS.
Welches ist genau gleichbedeutend mit:
Das bedeutet, dass die 'Feldaufteilung' mit einem Standard-IFS-Wert identisch oder nicht festgelegt ist.
Das bedeutet NICHT, dass IFS unter allen Bedingungen gleich funktioniert. Genauer gesagt, setzt die Ausführung
OldIFS=$IFS
die VariableOldIFS
auf null und nicht auf die Standardeinstellung. Wenn Sie versuchen, IFS zurückzusetzen,IFS=OldIFS
wird IFS auf Null gesetzt, nicht so wie zuvor. Achtung !!.F3: (Teil b) Stellen Sie IFS wieder her.
Für zsh, ksh und bash (AFAIK) kann IFS auf den Standardwert gesetzt werden:
Fertig, Sie brauchen nichts weiter zu lesen.
Wenn Sie jedoch IFS für sh zurücksetzen müssen, kann dies komplex werden.
Werfen wir einen Blick von der einfachsten bis zur vollständigen Ausführung ohne Nachteile (außer Komplexität).
1.- IFS deaktivieren.
Wir könnten nur
unset IFS
(Lesen Sie Q3 Teil a, oben.).2.- Zeichen tauschen.
Um dieses Problem zu umgehen, können Sie den Wert von "Tab" und "Newline" austauschen, um das Festlegen des IFS-Werts zu vereinfachen. Dies funktioniert dann auf die gleiche Weise.
Setzen Sie IFS auf <Leerzeichen> <Zeilenumbruch> <Tabulator> :
3.- Ein einfaches? Lösung:
Wenn untergeordnete Skripte vorhanden sind, für die IFS korrekt festgelegt werden muss, können Sie immer manuell schreiben:
Wo die manuell eingegebene Sequenz lautete:,
IFS=
'spacetabnewline'Sequenz, die oben tatsächlich korrekt eingegeben wurde (Wenn Sie eine Bestätigung benötigen, bearbeiten Sie diese Antwort). Das Kopieren / Einfügen aus Ihrem Browser wird jedoch unterbrochen, da der Browser das Leerzeichen ausblendet. Es ist schwierig, den Code wie oben beschrieben freizugeben.4.- Komplettlösung.
Das Schreiben von Code, der sicher kopiert werden kann, beinhaltet normalerweise eindeutige druckbare Escapezeichen.
Wir brauchen Code, der den erwarteten Wert "produziert". Aber selbst wenn dieser Code konzeptionell korrekt ist, wird KEIN Trailing gesetzt
\n
:Dies liegt daran, dass unter den meisten Shells alle nachfolgenden Zeilenumbrüche
$(...)
oder`...`
Befehlsersetzungen bei der Erweiterung entfernt werden.Wir müssen einen Trick für sh verwenden:
Eine alternative Möglichkeit kann sein, IFS als Umgebungswert aus bash festzulegen (zum Beispiel) und dann sh (die Versionen davon, die IFS akzeptieren, um über die Umgebung festgelegt zu werden) wie folgt aufzurufen:
Kurz gesagt, es ist ein merkwürdiges Abenteuer, IFS auf Standard zurückzusetzen.
F4: Im aktuellen Code:
Erstens: Ich weiß nicht, ob der
echo $line
(mit dem var NICHT zitierte) Schweinswal da ist oder nicht. Es führt eine zweite Ebene der 'Feldaufteilung' ein, die es beim Lesen nicht gibt. Also werde ich beides beantworten. :)Mit diesem Code (so könnte man das bestätigen). Du brauchst das nützliche xxd :
Ich bekomme:
Der erste Wert ist genau der richtige Wert von
IFS=
'spacetabnewline'Die nächste Zeile enthält alle
$a
Hexadezimalwerte, die die Variable hat, und am Ende eine neue Zeile '0a', die jedem Lesebefehl zugewiesen wird.Die nächste Zeile, für die IFS null ist, führt keine 'Feldaufteilung' durch, aber die neue Zeile wird entfernt (wie erwartet).
Da IFS ein Leerzeichen enthält, entfernen Sie in den nächsten drei Zeilen die ursprünglichen Leerzeichen und setzen Sie die var-Zeile auf den verbleibenden Kontostand.
Die letzten vier Zeilen zeigen, was eine Variable ohne Anführungszeichen bewirkt. Die Werte werden auf die (mehreren) Felder aufgeteilt und gedruckt als:
bar,baz,qux,
quelle
unset IFS
löscht IFS, auch wenn danach angenommen wird, dass IFS "\ t \ n" ist:Getestet auf den Bash-Versionen 4.2.45 und 3.2.25 mit dem gleichen Verhalten.
quelle
unset
vonIFS
, wie hier in den Kommentaren der akzeptierten Antwort erklärt.