Warum gibt "A = 10 echo $ A" keine 10 aus?


25

Dieser Befehl:

A=10 echo $A

druckt eine leere Zeile. Warum nicht 10? Warum funktioniert die temporäre Umgebungseinstellung nicht?

Ich möchte eher den Grund und die Erklärung als die Lösung kennen.

ich benutzte

LANG=C gcc ...

Um gcc zu erzwingen, verwenden Sie die Fallback-Sprache (Englisch) anstelle der Systemsprache (Chinesisch). Daher gehe ich davon aus, dass ein VAR=valuePräfix eine temporäre Umgebung für den darauf folgenden Befehl erstellt. Aber es sieht so aus, als hätte ich ein Missverständnis.

Antworten:


22

Es ist eine Frage der Reihenfolge, in der die verschiedenen Schritte zum Auswerten eines Befehls ausgeführt werden.

A=10 echo $Aparst zuerst den Befehl in einen einfachen Befehl, der aus drei Wörtern besteht A=10, echound $A. Dann wird jedes Wort durch eine Variable ersetzt, dh die Umwandlung variabler Erweiterungen $Ain ihre Werte (ich lasse Schritte aus, die nichts Sichtbares bewirken).

Wenn Ader Wert foozunächst, ist das Ergebnis der Expansionsschritte ein einfacher Befehl, der noch drei Worte hat: A=10, echound foo. (Die Shell merkt sich zu diesem Zeitpunkt auch, welche Zeichen ursprünglich in Anführungszeichen standen - in diesem Fall keine.) Der nächste Schritt besteht darin, den Befehl auszuführen. Da A=10mit einem gültigen Variablennamen gefolgt von einem Gleichheitszeichen begonnen wird, wird dies als Zuweisung behandelt. Die Variable Awird 10während der Ausführung des Befehls sowohl in der Shell als auch in der Umgebung auf gesetzt. (Normalerweise müssen Sie schreiben export A, um Ain der Umgebung und nicht nur als Shell-Variable zu haben. Dies ist eine Ausnahme.) Das nächste Wort ist keine Zuweisung, daher wird es als Befehlsname behandelt (es ist ein eingebauter Befehl). DasechoKommando ist von keiner Variablen abhängig, A=10 echo $Ahat also genau die gleiche Wirkung wie echo $A.

Wenn Sie eine Variable nur für die Dauer eines Befehls festlegen möchten, aber die Zuweisung bei der Ausführung des Befehls berücksichtigen möchten, können Sie eine Unterschale verwenden. Eine in Klammern gesetzte Subshell führt alle Statusänderungen (Variablenzuweisungen, aktuelles Verzeichnis, Funktionsdefinitionen usw.) lokal in der Subshell durch.

(A=10; echo $A)

Nehmen Sie dies vor, export A=10wenn Sie die Variable in die Umgebung exportieren möchten, damit sie von externen Programmen gesehen wird.


Danke, kann ich sagen A=10 (echo $A)und bekommen 10?
Earth Engine

2
@EarthEngine Nein, das wäre ein Syntaxfehler. Die Zuweisung muss zu Beginn eines einfachen Befehls erfolgen (dh nur ein Befehlsname und einige Parameter sowie optional einige anfängliche Zuweisungen und einige Umleitungen). A=10; (echo $A)gibt 10aber auch Sets Afür den Rest des Skripts aus.
Gilles 'SO- hör auf böse zu sein'

2
@EarthEngine Aber du kannst sagen A=10 eval 'echo $A'. Die einfachen Anführungszeichen werden nicht $Amehr interpretiert, bis die gesamte Zeile ausgewertet ist. Zu diesem Zeitpunkt ist A = 10. Ich halte diese Antwort für richtiger als die akzeptierte.
Oli

Ich denke das ist die richtige Erklärung. Der Grund für das Verhalten ist nur die Reihenfolge, in der die Erweiterung $Aund Zuordnung von Astattfindet. ZB A=5; A=6 let 'a=A'; echo $akehrt 6nicht zurück 5und ich glaube nicht, dass leteine Subshell startet, da es sich um einen eingebauten Befehl handelt.
David Ongaro

@EarthEngine: Wenn es gesagt wird, dass die richtige Erklärung der ist , um die Bewertung, kann es irreführend sein: A=10 echo $Awird nicht gesetzt A=10danach für alle Befehle, auch wenn sie in verschiedenen Linien sind (wenn eindeutig die Zuordnung wurde bereits ausgewertet). Es geht nicht um Ordnung, es geht um Umfang
MestreLion

37

Wenn Sie , LANG=C gcc ... was passiert , ist , dass die Schale LANG für setzt gcc‚s Umwelt nur , und nicht für die aktuelle Umgebung selbst ( siehe Hinweis ). Ist also nach dem gccBeenden LANGwieder auf den vorherigen Wert (oder nicht festgelegt).

Außerdem ersetzt bei Verwendung A=10 echo $Adie Shell $ A, nicht Echo, und diese Ersetzung (als "Erweiterung" bezeichnet) erfolgt, bevor die Anweisung (einschließlich der Zuweisung) ausgewertet wird, damit der erwartete AWert bereits festgelegt werden kann in der aktuellen Umgebung vor dieser Aussage.

Das ist der Grund, warum A=10 echo $Anicht wie erwartet funktioniert: A=10Wird für Echo festgelegt, aber Echo ignoriert intern den Wert der Umgebungsvariablen A. Und $Awird durch den in der aktuellen Shell festgelegten Wert ersetzt (bei dem es sich um keinen handelt) und dann als Argument an Echo übergeben.

So Ihre Annahme richtig ist: VAR=value command tut Arbeit, aber dies ist nur relevant , wenn commandintern verwendet VAR. Wenn nicht, können Sie immer noch valueein Argument an übergeben command, aber die Argumente werden durch die aktuelle Shell ersetzt, sodass sie vor der Verwendung festgelegt werden müssen:VAR=value; command "$VAR"

Wenn Sie wissen, wie man ein ausführbares Skript erstellt, können Sie dies als Test versuchen:

#!/bin/sh
echo "1st argument is $1"
echo "A is $A"

Speichern Sie es als testscriptund versuchen Sie:

$ A=5; A=10 testscript "$A"; echo "$A"
1st argument is 5
A is 10
5

Last but not least, es lohnt sich, den Unterschied zwischen Wissen Shell und Umwelt Variablen und Programm Argumente .

Hier sind einige gute Referenzen:

.

(*) Hinweis: technisch die Schale nicht in der aktuellen Umgebung gesetzt auch, und hier ist der Grund: Einige Befehle, wie echo, readund testsind Schale builtins , und als solche sie laichen kein Kind - Prozess. Sie laufen in der aktuellen Umgebung. Die Shell kümmert sich jedoch nur darum, dass die Zuweisung so lange dauert, bis der Befehl ausgeführt wird. In der Praxis ist der Effekt also derselbe: Die Zuweisung wird nur von diesem einzelnen Befehl gesehen.


2
Diese Erklärung ist tatsächlich falsch, obwohl sie in allen bis auf einige Eckfälle zu der richtigen Schlussfolgerung führt. Die wahre Erklärung ist die Reihenfolge der Erweiterung: $AWird ausgewertet, bevor die Zuordnung erfolgt. Ich denke, Ihre Erklärung schlägt nur bei regulären eingebauten Dienstprogrammen fehl, deren Verhalten vom Wert der Variablen abhängt: Die eingebauten sehen den zugewiesenen Wert. Ein häufiges Beispiel ist IFS=: read one two three rest, das durch Doppelpunkte getrennte Felder liest: Der readeingebaute Code sieht den Wert von IFS.
Gilles 'SO- hör auf böse zu sein'

"Nicht für die aktuelle Shell selbst" ist falsch: Die Variable ist in der aktuellen Shell gesetzt, aber sie hält nur für den aktuellen einfachen Befehl. echowürde den Wert 10für sehen A, wenn es interessiert.
Gilles 'SO- hör auf böse zu sein'

@Gilles: vielen dank für die aufklärung! Ich war mir dieser Subtilität nicht bewusst. Also, wenn ich das richtig verstanden, bash hat zu Satz für die aktuelle Umgebung sonst builtins (die keine neue laichen pidwürde) nicht die Zuordnung als andere „regurlar“ Befehle sehen würden. Es wird jedoch nach Ausführung des Befehls deaktiviert , um den Zuweisungsbereich einzuschränken . Ist das richtig, werde ich meine Antwort entsprechend korrigieren. PS: Formalitäten beiseite, ich denke immer noch eine Antwort auf den Umfang Aspekt hervorheben sollte, nicht Auswerteauftrag, sonst könnte man denken , A=10 test; echo $Adruckt 10
MestreLion

3

Eine möglicherweise saubere Möglichkeit, das zu tun, was Sie anscheinend wünschen, ist die Ausgabe des Befehls:

A=10 eval 'echo $A'

Wodurch die Substitution des Werts 10 an die Stelle von $ A in einen späteren Kontext verschoben wird (dh "innerhalb" der Auswertung, die bereits über die Zuordnung Bescheid weiß). Beachten Sie, dass die einfachen Anführungszeichen wichtig sind. Solch ein Konstrukt kommuniziert sauber die Zuweisung zu Ihrem gewünschten Befehl (in diesem Fall Echo), ohne das Risiko einzugehen, Ihre aktuelle Umgebung zu verschmutzen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.