Ich versuche herauszufinden, wie IFS Worttrennung in bash beeinflusst. Das Verhalten ist kontextabhängig in einer Weise, die nicht mit der Intuition der Worttrennung übereinstimmt.
Die allgemeinen Ideen scheinen einfach genug zu sein. Zitieren von der Bash-Man-Seite:
Die Shell behandelt jedes IFS-Zeichen als Trennzeichen und teilt die Ergebnisse der anderen Erweiterungen in Wörter für diese Zeichen auf. ... Beachten Sie, dass keine Aufspaltung ausgeführt wird, wenn keine Erweiterung stattfindet.
Dies kann leicht verifiziert werden, indem beispielsweise die IFS-Variable auf ',' gesetzt wird und eine Shell-Funktion mit einer durch Komma getrennten Argumentliste aufgerufen wird.
%Vor%Wie erwartet, führt dies zu drei verschiedenen Argumenten für echo_n
%Vor%Der direkte Aufruf von echo_n mit der kommagetrennten Liste schlägt fehl, weil keine Erweiterung ausgelöst wird.
%Vor%führt zu
%Vor%Bis hierher scheinen die Dinge ziemlich verzerrt, aber ich kann meinen Kopf um sie legen. Wenn wir dem Bild For-Schleifen hinzufügen, werden die Dinge haariger.
%Vor%führt zu
%Vor%was den Zweck der for-Schleife besiegt.
Nun kann ich die IFS-Worttrennung dort, wo ich es möchte, durch einen von mehreren Bash-Tricks erzwingen, die eine Art von Expansion erzwingen. Zum Beispiel:
%Vor%führt zu
%Vor%(Der Trick besteht darin, eine undefinierte Variable NO_VAR mit einem Standardwert auszuwerten.)
Ein weiterer ähnlicher Trick, der sich auf die Befehlsersetzung stützt:
%Vor%Hier ist die Frage: Was ist die empfohlene, idiomatische Art, den Kontext zu steuern, in dem die IFS-Worttrennung durchgeführt wird?
Es ist wichtig zu wissen, warum das Folgende nicht funktioniert:
%Vor% Die Vorbefehlszuweisung für IFS
gilt nur für innerhalb echo_n
; foo,bar,baz
wird nicht auf ,
aufgeteilt, da jede Worttrennung an dieser Befehlszeile (oder deren Fehlen) vor echo_n
ausgeführt wird.
führt zu einer einzelnen Iteration, weil IFS
nur zum Teilen der Ergebnisse von Expansionen (und read
, siehe unten) verwendet wird, keine literalen Strings. Die Worttrennung, die von der Shell beim ersten Parsen einer Befehlszeile ausgeführt wird, ist effektiv so programmiert, dass sie nur auf Leerzeichen aufgeteilt wird.
Es ist nicht ganz klar, was Sie erreichen möchten, aber eine gute Faustregel besagt, dass Sie etwas falsch machen (oder zumindest suboptimal), wenn Sie den Wert von IFS
global setzen. Es gibt nur zwei Situationen, in denen ich mich daran erinnern kann, IFS
sinnvoll zu modifizieren:
IFS=, read -r a b c
, um eine Zeile mit Kommas in mehrere (hier 3) Teile aufzuteilen. Die Änderung in IFS
ist lokal für read
; Die gelesene Zeichenfolge wird intakt gelesen und intern nur von read
geteilt.
foo=$(IFS=.; echo "${foo[*]}")
, um die Elemente eines Arrays zu einer einzelnen Zeichenfolge mit einem .
als Trennzeichen zu verbinden. Beachten Sie, dass dies eine globale Änderung in IFS
ist, aber nur in einem globalen Bereich, der nach dem Ende der Befehlsersetzung verschwindet.
Bezogen auf Ihre for
loop-Beispiele, wenn Sie immer über etwas anderes als eine hartcodierte Liste (die die Erweiterung eines Arrays umfasst) iterieren möchten, möchten Sie wahrscheinlich eine while
Schleife mit read
verwenden. anstelle einer for
-Schleife, wie in Bash FAQ 001 beschrieben.
Nehmen Sie Ihre for
-Schleife hier, zum Beispiel:
Ich würde es stattdessen zuerst in ein Array aufteilen und dann mit for
:
Tags und Links bash