Q1. Feldaufteilung.
Ist Feldaufteilung dasselbe wie Wortaufteilung?
Ja, beide weisen auf die gleiche Idee hin.
Setzt IFS=''
das gleiche wie null, auch das gleiche wie eine leere Zeichenfolge?
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.
In der POSIX-Spezifikation habe ich Folgendes gelesen :
Wenn IFS nicht festgelegt ist, verhält sich die Shell so, als ob der Wert von IFS <Leerzeichen> <Tabulator> <Zeilenumbruch> ist .
Welches ist genau gleichbedeutend mit:
Mit einem unset IFS
soll sich die Shell so verhalten, als wäre IFS Standard.
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 Variable OldIFS
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.
Wie kann ich den Standardwert von IFS wiederherstellen? Angenommen, ich möchte den Standardwert von IFS wiederherstellen. Wie mache ich das? ( Genauer gesagt, wie verweise ich auf <tab> und <newline> ?)
Für zsh, ksh und bash (AFAIK) kann IFS auf den Standardwert gesetzt werden:
IFS=$' \t\n' # works with zsh, ksh, bash.
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> :
sh -c 'IFS=$(echo " \n\t"); printf "%s" "$IFS"|xxd' # Works.
3.- Ein einfaches? Lösung:
Wenn untergeordnete Skripte vorhanden sind, für die IFS korrekt festgelegt werden muss, können Sie immer manuell schreiben:
IFS = '
'
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
:
sh -c 'IFS=$(echo " \t\n"); printf "%s" "$IFS"|xxd' # wrong.
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:
sh -c 'IFS="$(printf " \t\nx")"; IFS="${IFS%x}"; printf "$IFS"|xxd' # Correct.
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:
env IFS=$' \t\n' sh -c 'printf "%s" "$IFS"|xxd'
Kurz gesagt, es ist ein merkwürdiges Abenteuer, IFS auf Standard zurückzusetzen.
F4: Im aktuellen Code:
Schließlich, wie würde dieser Code:
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
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 :
#!/bin/ksh
# Correctly set IFS as described above.
defIFS="$(printf " \t\nx")"; defIFS="${defIFS%x}";
IFS="$defIFS"
printf "IFS value: "
printf "%s" "$IFS"| xxd -p
a=' bar baz quz '; l="${#a}"
printf "var value : %${l}s-" "$a" ; printf "%s\n" "$a" | xxd -p
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf 'Values quoted :\n' "" # With values quoted:
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS default quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf '%s\n' "Values unquoted :" # Now with values unquoted:
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- unquoted : "
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS defau unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
Ich bekomme:
$ ./stackexchange-Understanding-IFS.sh
IFS value: 20090a
var value : bar baz quz -20202062617220202062617a20202071757a2020200a
IFS --x-- : bar baz quz -20202062617220202062617a20202071757a202020
Values quoted :
IFS null quoted : bar baz quz -20202062617220202062617a20202071757a202020
IFS default quoted : bar baz quz-62617220202062617a20202071757a
IFS unset quoted : bar baz quz-62617220202062617a20202071757a
IFS space quoted : bar baz quz-62617220202062617a20202071757a
Values unquoted :
IFS --x-- unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS null unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS defau unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS unset unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS space unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
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,
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.