Wann kann ich ein temporäres IFS für die Feldaufteilung verwenden?

19

Sagen Sie in bash, Sie haben var=a.b.c.dann:

$ IFS=. printf "%s\n" $var
a.b.c

Eine solche Verwendung von wird IFSjedoch beim Erstellen eines Arrays wirksam:

$ IFS=. arr=($var)
$ printf "%s\n" "${arr[@]}"
a
b
c

Das ist sehr praktisch, aber wo ist das dokumentiert? Ein schnelles Lesen der Abschnitte über Arrays oder das Aufteilen von Wörtern in der Bash-Dokumentation gibt in keiner Weise Hinweise. Eine Suche IFSin der einseitigen Dokumentation liefert ebenfalls keine Hinweise auf diesen Effekt.

Ich bin mir nicht sicher, wann ich dies zuverlässig tun kann:

IFS=x do something

Und erwarten Sie, dass dies die IFSFeldaufteilung beeinflusst.

muru
quelle

Antworten:

28

Die Grundidee ist , dass die VAR=VALUE some-commandSätze VARauf die VALUEfür die Ausführung von , some-commandwenn some-commandein externer Befehl, und es wird nicht mehr Phantasie als das. Wenn Sie diese Intuition mit etwas Wissen über die Funktionsweise einer Shell kombinieren, sollten Sie in den meisten Fällen die richtige Antwort finden. Die POSIX-Referenz lautet "Einfache Befehle" im Kapitel "Shell Command Language" .

Wenn some-commandes sich um einen externen Befehl handelt , VAR=VALUE some-commandentspricht dies env VAR=VALUE some-command. VARwird in die Umgebung von exportiert some-command, und sein Wert (oder das Fehlen eines Werts) in der Shell ändert sich nicht.

Wenn some-commandes sich um eine Funktion handelt , VAR=VALUE some-commandist dies äquivalent zu VAR=VALUE; some-command, dh die Zuweisung bleibt bestehen, nachdem die Funktion zurückgegeben wurde, und die Variable wird nicht in die Umgebung exportiert. Der Grund dafür hängt mit dem Design der Bourne-Shell (und später mit der Abwärtskompatibilität) zusammen: Sie hatte keine Möglichkeit, Variablenwerte um die Ausführung einer Funktion herum zu speichern und wiederherzustellen. Das Nichtexportieren der Variablen ist sinnvoll, da eine Funktion in der Shell selbst ausgeführt wird. Ksh (einschließlich ATT ksh93 und pdksh / mksh), bash und zsh implementieren jedoch das nützlichere Verhalten, wenn VARes nur während der Ausführung der Funktion festgelegt wird (es wird auch exportiert). In ksh geschieht dies, wenn die Funktion mit der ksh-Syntax definiert istfunction NAME …nicht, wenn es mit der Standardsyntax definiert ist NAME (). In Bash wird dies nur im Bash-Modus durchgeführt, nicht im POSIX-Modus (wenn mit ausgeführt POSIXLY_CORRECT=1). In zsh geschieht dies, wenn die posix_builtinsOption nicht gesetzt ist. Diese Option ist standardmäßig nicht aktiviert, wird jedoch von emulate shoder aktiviert emulate ksh.

Wenn some-commandes sich um eine integrierte Funktion handelt, hängt das Verhalten vom Typ der integrierten Funktion ab. Spezielle Builtins verhalten sich wie Funktionen. Spezielle integrierte Funktionen müssen in der Shell implementiert werden, da sie die Status-Shell beeinflussen (z. B. den breakKontrollfluss cdbeeinflussen, das aktuelle Verzeichnis setbeeinflussen, Positionsparameter und -optionen beeinflussen…). Andere eingebaute Funktionen dienen nur der Leistung und der Benutzerfreundlichkeit (meistens - z. B. kann die Bash-Funktion printf -vnur von einer eingebauten Funktion implementiert werden) und verhalten sich wie externe Befehle.

Die Zuweisung erfolgt nach der Alias-Erweiterung. Wenn some-commandes sich also um einen Alias ​​handelt , erweitern Sie ihn zuerst, um herauszufinden, was passiert.

Beachten Sie, dass die Zuweisung in allen Fällen ausgeführt wird, nachdem die Befehlszeile analysiert wurde, einschließlich der Variablenersetzung in der Befehlszeile. Also var=a; var=b echo $vardruckt a, weil $varausgewertet wird, bevor die Zuordnung erfolgt. Und IFS=. printf "%s\n" $varnutzt somit den alten IFSWert zum Teilen $var.

Ich habe alle Arten von Befehlen behandelt, aber es gibt noch einen weiteren Fall: Wenn kein Befehl ausgeführt werden muss , dh wenn der Befehl nur aus Zuweisungen (und möglicherweise Umleitungen) besteht. In diesem Fall bleibt die Zuordnung bestehen . VAR=VALUE OTHERVAR=OTHERVALUEist äquivalent zu VAR=VALUE; OTHERVAR=OTHERVALUE. So nach IFS=. arr=($var), IFSbleibt auf .. Da Sie $IFSin der Zuordnung zu arrmit der Erwartung verwenden könnten, dass es bereits seinen neuen Wert hat, ist es sinnvoll, dass der neue Wert von IFSfür die Erweiterung von verwendet wird $var.

Zusammenfassend können Sie nur IFSfür die temporäre Feldaufteilung verwenden:

  • durch Starten einer neuen Shell oder einer Subshell (z. B. third=$(IFS=.; set -f; set -- $var; echo "$3")ist eine komplizierte Methode, mit der third=${var#*.*.}Ausnahme, dass sie sich anders verhalten, wenn der Wert von varweniger als zwei .Zeichen enthält);
  • in KSH, mit IFS=. some-functiondenen some-functionmit der KSH Syntax definiert ist function some-function …;
  • in bash und zsh, IFS=. some-functionsolange sie im einheitlichen Modus im Gegensatz zum Kompatibilitätsmodus ausgeführt werden.
Gilles 'SO - hör auf böse zu sein'
quelle
" IFSbleibt auf ." Eek. Nachdem ich den ersten Teil gelesen habe, macht das Sinn, aber bevor ich diesen Fragebogen gepostet habe, hätte ich das nicht erwartet.
muru
1
Dies ist eine Antwort auf eine andere Frage
schily
Einige zusätzliche Erklärungen in dieser Antwort von vor einigen Jahren .
Ti Strga
6

Die Antwort von @Gilles ist wirklich großartig, erklärt er (im Detail) ein komplexes Problem.

Ich glaube jedoch, dass die Antwort, warum dieser Befehl:

$ IFS=. printf "%s\n" $var
a.b.c

Funktioniert wie es funktioniert, ist die einfache Idee, dass die gesamte Befehlszeile analysiert wird, bevor sie ausgeführt wird. Und dass jedes "Wort" einmal von der Shell verarbeitet wird.
Die Zuweisungen sind wie IFS=.verzögert (Schritt 4 ist der letzte):

4.- Jede Variablenbelegung soll erweitert werden ...

Bis kurz bevor der Befehl ausgeführt wird und alle Erweiterungen in Argumenten zuerst verarbeitet werden, um diese ausführbare Zeile zu erstellen:

$ IFS=. printf "%s\n" a.b.c           ## IFS=. goes to the environment.
a.b.c

Der Wert von $varwird mit dem "alten" IFS bis erweitert, a.b.cbevor dem Befehl printfdie Argumente "%s\n"und gegeben werden a.b.c.

Eval

Eine Verzögerungsstufe kann eingeführt werden durch eval:

$ IFS=. eval printf "'%s\n'" \$var
a
b
c

Die Zeile wird analysiert (1. Mal) und 'IFS =.' ist auf die Umgebung wie folgt eingestellt:

$ printf '%s\n' $var

Dann wird es noch einmal analysiert:

$ printf '%s\n' a b c

Und dazu ausgeführt:

a
b
c

Der Wert von $var(abc) wird geteilt durch den Wert des IFS in Gebrauch: ..

Umgebung

Der komplexe und knifflige Teil ist das, was in der Umwelt gilt, wenn !!!

Das wird im ersten Teil der Antwort von Gilles sehr gut erklärt.

Mit einem zusätzlichen Detail.

Wenn dieser Befehl ausgeführt wird:

$ IFS=. arr=($var)

Der Wert von IFS bleibt in der gegenwärtigen Umgebung erhalten, ja:

$ printf '<%s>  ' "${arr[@]}" "$IFS"
<a>  <b>  <c>  <.> 

IFS für eine einzelne Anweisung.

Es könnte jedoch vermieden werden: Setzen von IFS für eine einzelne Anweisung

$ IFS=. command eval arr\=\(\$var\)

$  printf '<%s>  ' "${arr[@]}" "$IFS"
<a>  <b>  <c>  < 
> 
Gemeinschaft
quelle
2

Ihre Frage zu

var=a.b.c
IFS=. printf "%s\n" $var

ist ein Eckfall.

Dies liegt daran, dass der macro expansionBefehl in ausgeführt wird, bevor die Shell-Variable IFS=.festgelegt wird.

Mit anderen Worten: Wenn $varerweitert, wird der vorherige IFSWert aktiv und dann auf IFSgesetzt '.'.

schily
quelle