In einigen Shells (einschließlich bash
):
IFS=: command eval 'p=($PATH)'
(mit bash
können Sie die command
if not in sh / POSIX-Emulation weglassen ). Beachten Sie jedoch, dass bei der Verwendung von Variablen ohne Anführungszeichen in der Regel auch dies erforderlich set -f
ist und in den meisten Shells kein lokaler Bereich dafür vorhanden ist.
Mit zsh können Sie Folgendes tun:
(){ local IFS=:; p=($=PATH); }
$=PATH
ist zu Wort Spaltung zu zwingen , die nicht standardmäßig in getan wird zsh
(Globbing auf variable Expansion erfolgt auch nicht , so dass Sie nicht brauchen , set -f
es sei denn in sh - Emulation).
(){...}
(oder function {...}
) werden als anonyme Funktionen bezeichnet und in der Regel zum Festlegen eines lokalen Bereichs verwendet. Mit anderen Shells, die den lokalen Funktionsumfang unterstützen, können Sie Folgendes tun:
e() { eval "$@"; }
e 'local IFS=:; p=($PATH)'
Um einen lokalen Bereich für Variablen und Optionen in POSIX-Shells zu implementieren, können Sie auch die unter https://github.com/stephane-chazelas/misc-scripts/blob/master/locvar.sh bereitgestellten Funktionen verwenden . Dann können Sie es verwenden als:
. /path/to/locvar.sh
var=3,2,2
call eval 'locvar IFS; locopt -f; IFS=,; set -- $var; a=$1 b=$2 c=$3'
(Übrigens ist es ungültig, so zu teilen $PATH
, außer in, zsh
wie in anderen Shells, IFS ist ein Feldtrennzeichen, kein Feldtrennzeichen).
IFS=$'\n' a=($str)
Ist nur zwei Aufgaben, die einer nach dem anderen genau gefallen a=1 b=2
.
Eine Erläuterung zu var=value cmd
:
Im:
var=value cmd arg
Die Shell wird /path/to/cmd
in einem neuen Prozess ausgeführt und passiert cmd
und arg
in argv[]
und var=value
in envp[]
. Das ist nicht wirklich eine Variablenzuweisung, sondern die Übergabe von Umgebungsvariablen an den ausgeführten Befehl. In der Bourne- oder Korn-Shell set -k
können Sie mit sogar schreiben cmd var=value arg
.
Dies gilt nicht für integrierte Funktionen oder Funktionen, die nicht ausgeführt werden . In der Bourne - Shell, in var=value some-builtin
, var
endet danach eingestellt wird, genau wie bei var=value
allein. Das bedeutet zum Beispiel, dass das Verhalten von var=value echo foo
(was nicht nützlich ist) abhängig davon variiert, ob echo
es eingebaut ist oder nicht.
POSIX und / oder hat dies ksh
dahingehend geändert, dass das Bourne-Verhalten nur für eine Kategorie von Buildins auftritt, die als spezielle Buildins bezeichnet werden . eval
ist ein spezielles eingebautes, read
nicht. Für nicht spezielle builtin, var=value builtin
setzt var
nur für die Ausführung des builtin , die es ähnlich wie wenn ein externer Befehl verhalten macht , ist gestartet wird.
Der command
Befehl kann verwendet werden, um das spezielle Attribut dieser speziellen eingebauten Elemente zu entfernen . Was POSIX jedoch übersehen hat, ist, dass für die eval
und .
Builtins die Shells einen Variablenstapel implementieren müssten (obwohl sie die local
oder typeset
bereichsbeschränkenden Befehle nicht angeben ), weil Sie Folgendes tun könnten:
a=0; a=1 command eval 'a=2 command eval echo \$a; echo $a'; echo $a
Oder auch:
a=1 command eval myfunction
mit myfunction
einer Funktion, die verwendet oder einstellt $a
und möglicherweise aufruft command eval
.
Das war wirklich ein Versehen, weil ksh
(worauf die Spezifikation hauptsächlich basiert) es nicht implementiert hat (und AT & T ksh
und zsh
immer noch nicht), aber heutzutage, mit Ausnahme dieser beiden, implementieren die meisten Shells es. Das Verhalten der Muscheln variiert jedoch in folgenden Punkten:
a=0; a=1 command eval a=2; echo "$a"
obwohl. Die Verwendung local
von Shells, die dies unterstützen, ist eine zuverlässigere Möglichkeit, den lokalen Bereich zu implementieren.
IFS=: command eval …
setztIFS
nur für die Dauer dereval
, wie von POSIX vorgeschrieben, in Bindestrich, Pdksh und Bash, aber nicht in Ksh 93u. Es ist ungewöhnlich, zu sehen, dass ksh die seltsame Ausnahme ist.Standardmäßiges Speichern und Wiederherstellen aus "The Unix Programming Environment" von Kernighan und Pike:
quelle
$IFS
wenn sie zuvor deaktiviert wurde.$'\t\n'' '
: wiki.bash-hackers.org/syntax/expansion/…$' \t\n'
. Der Raum muss der erste sein, für den er verwendet wird"$*"
. Beachten Sie, dass dies bei allen Bourne-ähnlichen Shells der Fall ist.Fügen Sie Ihr Skript in eine Funktion ein und rufen Sie diese Funktion auf, indem Sie die Befehlszeilenargumente an sie übergeben. Da IFS lokal definiert ist, wirken sich Änderungen daran nicht auf das globale IFS aus.
quelle
Für diesen Befehl:
Es gibt eine alternative Lösung: Um der ersten
IFS=$'\n'
Anweisung ( ) einen auszuführenden Befehl (eine Funktion) zu geben:Das versetzt IFS in die Umgebung, um Split aufzurufen, wird aber in der aktuellen Umgebung nicht beibehalten.
Dies vermeidet auch den immer riskanten Einsatz von eval.
quelle
$IFS
Einstellung$'\n'
danach immer noch auf POSIX gesetzt.Die vorgeschlagene Antwort von @helpermethod ist sicherlich ein interessanter Ansatz. Es ist aber auch eine Art Falle, da der Gültigkeitsbereich der lokalen Variablen in BASH vom Aufrufer bis zur aufgerufenen Funktion reicht. Wenn Sie daher IFS in main () setzen, bleibt dieser Wert für die von main () aufgerufenen Funktionen erhalten. Hier ist ein Beispiel:
Und die Ausgabe ...
Wenn IFS, das in main () deklariert wurde, in func () noch nicht im Gültigkeitsbereich war, wurde das Array in func () B nicht richtig analysiert.
Welches ist, was Sie erhalten sollten, wenn IFS aus dem Anwendungsbereich herausgegangen war.
Eine weitaus bessere Lösung ist meiner Meinung nach, auf Änderungen oder das Vertrauen in IFS auf globaler / lokaler Ebene zu verzichten. Erstellen Sie stattdessen eine neue Shell und spielen Sie mit IFS. Wenn Sie beispielsweise func () in main () wie folgt aufrufen, übergeben Sie das Array als Zeichenfolge mit einem umgekehrten Schrägstrich als Feldtrennzeichen:
... diese Änderung an IFS wird nicht in func () wiedergegeben. Das Array wird als String übergeben:
... aber innerhalb von func () bleibt das IFS "/" (wie in main () gesetzt), sofern es nicht lokal in func () geändert wird.
Weitere Informationen zum Isolieren von Änderungen an IFS finden Sie unter den folgenden Links:
Wie konvertiere ich eine Bash-Array-Variable in einen mit Zeilenumbrüchen getrennten String?
Bash-String zum Anordnen mit IFS
Hinweise und Tipps zur allgemeinen Programmierung von Shell-Skripten - Siehe "HINWEIS zur Verwendung von Sub-Shells ..."
quelle
IFS=$'\n' declare -a astr=(...)
perfekt, danke!Dieser Ausschnitt aus der Frage:
wird als zwei separate Zuweisungen globaler Variablen interpretiert, die von links nach rechts ausgewertet werden, und ist äquivalent zu:
oder
Dies erklärt sowohl, warum das Global
IFS
geändert wurde, als auch warum die Wortaufteilung$str
in Array-Elemente mit dem neuen Wert von durchgeführt wurdeIFS
.Sie könnten versucht sein, eine Subshell zu verwenden, um den Effekt der
IFS
Änderung wie folgt einzuschränken :Sie werden jedoch schnell bemerken, dass die Änderung von
a
auch auf die Subshell beschränkt ist:Als nächstes würden Sie versucht sein, IFS mit der Lösung aus dieser vorherigen Antwort von @msw zu speichern / wiederherzustellen oder
local IFS
eine von @helpermethod vorgeschlagene Funktion zu verwenden. Aber ziemlich bald bemerken Sie, dass Sie in allerlei Schwierigkeiten geraten, besonders wenn Sie ein Bibliotheksautor sind, der robust gegen schlechtes Benehmen beim Aufrufen von Skripten sein muss:IFS
anfangs nicht gesetzt war?set -u
(akaset -o nounset
) laufen ?IFS
nur lesbar gemacht wurdedeclare -r IFS
?trap
Handler) erforderlich ist?Bitte nicht speichern / wiederherstellen IFS. Halten Sie sich stattdessen an temporäre Änderungen:
Verwenden Sie, um die Variablenänderung auf einen einzelnen Befehl, einen integrierten Befehl oder einen Funktionsaufruf zu beschränken
IFS="value" command
.:
Verwenden Sie zum Einlesen mehrerer Variablen durch Aufteilen auf ein bestimmtes Zeichen ( im Folgenden als Beispiel verwendet) Folgendes:Verwenden Sie zum Einlesen eines Arrays (anstelle von
array_var=( $str )
):Begrenzen Sie die Auswirkungen des Änderns der Variablen auf eine Unterschale.
So geben Sie die durch Komma getrennten Elemente eines Arrays aus:
So erfassen Sie das in einer Zeichenfolge:
quelle
Die einfachste Lösung besteht darin, eine Kopie des Originals zu
$IFS
erstellen, wie z. B. in der Antwort von msw. Diese Lösung unterscheidet jedoch nicht zwischen einem nicht festgelegtenIFS
und einemIFS
Satz, der der leeren Zeichenfolge entspricht, was für viele Anwendungen wichtig ist. Hier ist eine allgemeinere Lösung, die diese Unterscheidung erfasst:quelle