Wie kann ich eine Umgebungsvariable in der Befehlszeile festlegen und in Befehlen anzeigen lassen?

152

Wenn ich renne

export TEST=foo
echo $TEST

Es gibt foo aus.

Wenn ich renne

TEST=foo echo $TEST

Es tut nicht. Wie kann ich diese Funktionalität erhalten, ohne Export oder ein Skript zu verwenden?

ashleysmithgpu
quelle
Vorsichtig, es gibt mehr in dieser Geschichte, als zunächst erscheint. Ich lade Sie ein, meine Antwort zu überprüfen.
Jasonleonhard

Antworten:

180

Dies liegt daran, dass die Shell die Variable in der Befehlszeile erweitert, bevor sie den Befehl ausführt, und die Variable zu diesem Zeitpunkt nicht vorhanden ist. Wenn du benutzt

TEST=foo; echo $TEST

es wird klappen.

exportwird die Variable in der Umgebung von später ausgeführten Befehlen erscheinen lassen (wie dies in Bash funktioniert, siehe help export). Wenn die Variable nur in der Umgebung eines Befehls angezeigt werden soll, verwenden Sie das, was Sie versucht haben:

TEST=foo your-application
peterph
quelle
1
Was ist mit / usr / bin / env? das geht auch nicht, weißt du warum?
Benubird
5
Aus dem gleichen Grund - die Shell erweitert die, $TESTbevor die Befehlszeile ausgeführt wird. Sobald das ausgeführt echowird (beachten Sie auch, dass dies echonormalerweise in den integrierten Shell-Befehl und nicht in übersetzt wird /bin/echo), wird die in der Umgebung festgelegte Variable angezeigt. Weist echo $TESTjedoch nicht echoan, den Inhalt einer Variablen TESTaus ihrer Umgebung auszugeben . Es weist die Shell an, echomit Argument zu arbeiten, das dem entspricht, was sich aktuell in der aufgerufenen Variablen befindet TEST- und das sind zwei sehr unterschiedliche Dinge.
Peterph
@peterph Wenn die Shell die Variable in der Befehlszeile erweitert, bevor sie den Befehl ausführt, warum wird sie dann nicht erweitert var=value sh -c 'echo "$var"'?
Haccks
Wie ich Ihnen hier erklärt habe : Variablen werden in doppelte Anführungszeichen (zB "… $var …"), aber nicht in einfache Anführungszeichen (zB '… $var …') gesetzt. Da echo "$var"sich der gesamte String in einfachen Anführungszeichen befindet, wird er an die new ( sh -c) - Shell übergeben, ohne von der äußeren, interaktiven Shell interpretiert zu werden. … (Fortsetzung)
G-Man
(Fortsetzung)… Wie igal gestern sagte , gibt es zwei Parsing-Runden - obwohl ich glaube, dass igal Ihre Frage falsch interpretiert hat. Abgesehen von der Analyse, die von der übergeordneten Shell durchgeführt wird, gibt es keine zwei Parsing-Runden . Es gibt zwei Parsing-Runden - eine von der äußeren, interaktiven (übergeordneten) Shell und eine von der neuen ( sh -c) untergeordneten Shell.
G-Man
39

Ich vermute, Sie möchten Shell- Variablen haben, um einen begrenzten Bereich zu haben, anstatt Umgebungsvariablen. Umgebungsvariablen sind eine Liste von Zeichenfolgen, die bei ihrer Ausführung an Befehle übergeben werden .

Im

var=value echo whatever

Sie übergeben den var=valueString an die Umgebung, die das Echo empfängt. Tut echojedoch nichts mit seiner Umgebungsliste und wird sowieso in den meisten Shells echoeingebaut und daher nicht ausgeführt .

Wenn du geschrieben hättest

var=value sh -c 'echo "$var"'

Das wäre eine andere Sache gewesen. Hier übergeben wir var=valueden shBefehl und shverwenden zufällig seine Umgebung. Shells konvertieren jede der Variablen, die sie von ihrer Umgebung erhalten, in eine Shell-Variable, sodass die varUmgebungsvariable, die sie shempfängt, in eine $varVariable konvertiert wird. Wenn sie in dieser echoBefehlszeile erweitert wird, wird dies zu echo value. Da die Umgebung standardmäßig vererbt wird, echowird sie auch var=valuein ihrer Umgebung empfangen (oder würde ausgeführt werden), echokümmert sich aber auch hier nicht um die Umgebung.

Nun, wenn Sie, wie ich vermute, den Umfang der Shell-Variablen einschränken möchten, gibt es mehrere mögliche Ansätze.

Portabel (Bourne und POSIX):

(var=value; echo "1: $var"); echo "2: $var"

Das obige (...) startet eine Sub-Shell (ein neuer Shell-Prozess in den meisten Shells), daher wirkt sich jede dort deklarierte Variable nur auf diese Sub-Shell aus. Daher würde ich erwarten, dass der obige Code "1: value" ausgibt. und "2:" oder "2: was-war-vorher-gesetzt".

Mit den meisten Bourne-ähnlichen Shells können Sie Funktionen und das "lokale" Builtin verwenden:

f() {
  local var
  var=value
  echo "1: $var"
}
f
echo "2: $var"

Mit zsh können Sie Inline-Funktionen verwenden:

(){ local var=value; echo "1: $var"; }; echo "2: $var"

oder:

function { local var=value; echo "1: $var"; }; echo "2: $var"

Mit bash und zsh (aber nicht ash, pdksh oder AT & T ksh) funktioniert dieser Trick auch:

var=value eval 'echo "1: $var"'; echo "2: $var"

Eine Variante , die in ein paar mehr Granaten (funktioniert dash, mksh, yash) , aber nicht zsh(es sei denn , in sh/ kshEmulation):

var=value command eval 'echo "1: $var"'; echo "2: $var"

(Die Verwendung commandvor einem speziellen (hier eval) in POSIX-Shells beseitigt deren Besonderheit (hier bleiben die von ihnen zugewiesenen Variablen nach ihrer Rückkehr wirksam).)

Stéphane Chazelas
quelle
Für meine Zwecke ist dies die richtige Lösung. Ich möchte nicht, dass die Variable 'var' die Umgebung verschmutzt, wie es in der akzeptierten Antwort der Fall ist.
kronenpj
6

Sie tun es richtig, aber die Bash - Syntax ist einfach falsch zu interpretieren: Sie könnten denken , dass echo $TESTbewirkt , dass echoholen TESTenv var es dann drucken, es funktioniert nicht. So gegeben

export TEST=123

dann

TEST=456 echo $TEST

beinhaltet die folgende Sequenz:

  1. Die Shell analysiert die gesamte Befehlszeile und führt alle Variablensubstitutionen aus, sodass die Befehlszeile zu wird

    TEST=456 echo 123
  2. Es erstellt die vor dem Befehl festgelegten temporären Variablen, speichert also den aktuellen Wert von TESTund überschreibt ihn mit 456; Die Kommandozeile ist jetzt

    echo 123
  3. Es führt den verbleibenden Befehl aus, der in diesem Fall 123 an stdout ausgibt (der verbleibende Shell-Befehl hat also nicht einmal den Temp-Wert von verwendet TEST).

  4. Es stellt den Wert von wieder her TEST

Verwenden Sie stattdessen printenv, da keine Variablensubstitution erforderlich ist:

>> export TEST=123
>> printenv TEST
123
>> TEST=456 printenv TEST
456
>> printenv TEST && TEST=456 printenv TEST && TEST=789 printenv TEST && printenv TEST
123
456
789
123
>>
Oliver
quelle
+1 Ich fand printenvhilfreich für das Testen / Proof of Concept (verhält sich wie ein Skript, im Gegensatz zu echo)
Daryn
5

Sie können dies erreichen, indem Sie verwenden:

TEST=foo && echo $TEST
pradeepchhetri
quelle
6
In diesem Fall TEST=foowird als separate Anweisung ausgeführt - es wird nicht nur im Kontext von festgelegt echo.
Codeforester