Wie definiere ich in bash eine "Befehlsbereichsvariable"?

6

Ich lese das Buch über die Bash-Architektur und einer der Bash-Betreuer sagt:

Es gibt verschiedene variable Bereiche für Shell-Funktionsaufrufe und temporäre Bereiche für Variablen, die durch Zuweisungsanweisungen vor einem Befehl festgelegt werden . Wenn diese Zuweisungsanweisungen einem in der Shell integrierten Befehl vorangehen, muss die Shell beispielsweise die richtige Reihenfolge verfolgen, in der die Variablenreferenzen aufgelöst werden, und die verknüpften Bereiche ermöglichen es der bash, dies zu tun.

Ich kann nicht herausfinden, wie das geht. Dies druckt einen leeren Zeilenumbruch:

foo="bar" echo $foo

Dies druckt "bar" aber die foo Die Variable ist immer noch definiert, nachdem der Befehl beendet wurde, daher betrachte ich sie nicht als "Befehlsumfang":

foo="bar"; echo $foo

Wie definiere ich eine Variable mit Befehlsumfang?

Daniel Kaplan
quelle

Antworten:

6

Ihre erste Vermutung war richtig, aber Sie haben ein schlechtes Beispiel verwendet. Wenn die Shell den Befehl liest

foo=bar echo $foo

das erste, was es tut (oder zumindest eines der ersten Dinge) ist nach Variablenreferenzen zu suchen (wie $foo ) und bewerten sie. Dann es verarbeitet, was von der Linie übrig ist. (Dies kann ein bisschen zu stark vereinfacht werden; sehen bash(1) oder der Bash-Referenzhandbuch für Details.) Sie haben also den Befehl

echo  (nichts) 

mit laufen foo=bar; aber es ist zu spät; Es gibt nichts mehr zu sehen auf den Wert von $foo. Sie können dies mit der folgenden Sequenz demonstrieren:

$ foo=oldvalue
$ foo=bar echo "$foo"
oldvalue

Aber

foo=bar  Befehl 

ist der Weg zu gehen. Einige Beispiele, die funktionieren, sind:

foo=bar sh -c 'echo "$foo"'

und

foo=bar env

(Möglicherweise möchten Sie das durchleiten grep foo.)

Ich habe gerade den Satz bemerkt, den Sie zitiert haben, "Wenn diese Zuweisungsanweisungen einem in die Shell integrierten Befehl vorangehen, z. B. ...", schlägt vor, dass integrierte Befehle (wie z echo ) einen Sonderfall darstellen. Es ist (für mich zumindest) intuitiv, dass Sie nach diesem "Befehlsbereich" suchen müsste funktionieren, indem Sie in einem untergeordneten Prozess eine Umgebungsvariable setzen, und es ist nicht klar, wie es für einen integrierten Befehl funktionieren würde, der nicht in einem untergeordneten Prozess ausgeführt wird. Nicht viele integrierte Befehle greifen direkt auf die Umgebung zu. und ihre Interaktion mit Shell-Variablen ist manchmal unecht. Ich habe aber noch ein paar Beispiele gefunden, nach denen Sie vielleicht mehr suchen:

foo=bar eval 'echo $foo'

wird "bar" anzeigen, da die Bewertung von $foo wird verschoben. Es wird von der behandelt eval (eingebauter) Befehl anstelle des anfänglichen Parsing-Durchlaufs der Shell. Oder erstellen Sie eine Textdatei (zum Beispiel) showme.sh. Setzen Sie ein echo $foo (oder echo "$foo" ) Befehl hinein und sagen

foo=bar . showme.sh

Das ist wahrscheinlich, worüber Ihr Buch spricht, wenn es sagt: “… Die Shell muss die korrekte Reihenfolge verfolgen, in der Variablenreferenzen aufgelöst werden…” Irgendwie führt die Shell die Befehle aus showme.sh mit foo gleich bar. und kehrt dann zum vorherigen Wert von zurück foo (wenn überhaupt) wenn es zu seinem primären Eingang (dem Terminal) zurückkehrt.

G-Man
quelle
Wollen Sie damit sagen, dass es nicht funktioniert, weil das Echo ein integrierter Befehl ist? Wenn ja, sollten Sie das anders betonen. Ich bin mir nicht sicher, ob das der Hauptnahrungsmittel ist.
Daniel Kaplan
1
Nein, ich sage dasselbe, was Grawity sagt - es funktioniert nicht, weil die $foo wird "erweitert", bevor noch etwas passiert. Ich habe meine Antwort mit weiteren Gedanken zu integrierten Befehlen aktualisiert.
G-Man
1
@DocSalvager: Darauf bezog ich mich, als ich sagte: "Nicht viele integrierte Befehle greifen direkt auf die Umgebung zu", aber ich denke, ich hätte es deutlicher sagen können.
G-Man
1
echo '$foo' gibt den String aus $foo. cat '$foo' und ls -l '$foo’ Suchen Sie nach einer Datei namens $foo. grep '$foo' .bashrc sucht nach der Zeichenfolge $foo in deiner .bashrc Datei. Aber die Shell behandelt, im Gegensatz zu den anderen Programmen, etwas $ speziell; sh -c '$foo' sucht nach einer Umgebungsvariablen namens foo. Zum Beispiel, foo=date sh -c '$foo' wird ein laufen lassen date Befehl. (Vergleiche mit der Tatsache, dass cat '\0107' und ls -l '\0107’ Suchen Sie nach einer Datei namens \0107, und grep '\0107' sucht nach der Zeichenfolge \0107, aber echo -e '\0107' gibt den String aus G,… (Fortsetzung)
G-Man
1
(Fortsetzung)… weil echo Leckereien \ besonders wenn der -e Option ist vorgesehen.)
G-Man
5

Das Problem ist hier die variable Erweiterung ( $foo → value) geschieht vor der Auswertung des gesamten Befehls (put foo=bar in die Umwelt laufen echo ... ).

Es würde für Befehle funktionieren, die selbst auf die Umgebung zugreifen:

foo=bar env | grep ^foo

foo=bar declare -p foo

Bash hat nicht wirklich den richtigen Spielraum für das, was Sie suchen. Sie müssen den "Subprozess" -Trick verwenden, um einen neuen zu erhalten verarbeiten für den Befehl. (Natürlich bedeutet das nur Lesezugriff, Nein Änderungen erreichen das übergeordnete Element ...) Use ( ... ) So erstellen Sie eine Subshell:

(foo=bar; echo $foo)
grawity
quelle