Warum kann ich Variablen nicht als Präfix für einen Befehl zum Festlegen von Umgebungsvariablen verwenden?

11

Normalerweise ist es möglich, eine Umgebungsvariable für einen Befehl festzulegen, indem Sie ihn wie folgt voranstellen:

hello=hi bash -c 'echo $hello'

Ich weiß auch, dass wir eine Variable verwenden können, um einen beliebigen Teil eines Befehlsaufrufs wie folgt zu ersetzen:

$ cmd=bash
$ $cmd -c "echo hi" # equivalent to bash -c "echo hi"

Ich war sehr überrascht, als ich herausfand, dass Sie einem Befehl zum Festlegen einer Umgebungsvariablen keine Variable voranstellen können. Testfall:

$ prefix=hello=hi
$ echo $prefix # prints hello=hi
$ $prefix bash -c 'echo $hello'
hello=hi: command not found

Warum kann ich die Umgebungsvariable nicht mithilfe einer Variablen festlegen? Ist der Präfixteil ein besonderer Teil? Ich konnte es mit eval front zum Laufen bringen, aber ich verstehe immer noch nicht warum. Ich benutze Bash 4.4.

wbkang
quelle
1
Sie können envstattdessen verwenden eval, welches IIRC sicherer, aber langsamer ist.
Wjandrea

Antworten:

14

Ich vermute, das ist der Teil der Sequenz, der dich fängt:

Die Wörter, die keine Variablenzuweisungen oder Umleitungen sind, werden erweitert (siehe Shell-Erweiterungen). Wenn nach der Erweiterung noch Wörter übrig sind, wird das erste Wort als Name des Befehls verwendet, und die verbleibenden Wörter sind die Argumente

Dies stammt aus dem Bash-Referenzhandbuch im Abschnitt über die einfache Befehlserweiterung.

In diesem cmd=bashBeispiel werden keine Umgebungsvariablen festgelegt, und bash verarbeitet die Befehlszeile durch Parametererweiterung bash -c "echo hi".

In diesem prefix=hello=hiBeispiel gibt es im ersten Durchgang wieder keine Variablenzuweisungen, sodass die Verarbeitung mit der Parametererweiterung fortgesetzt wird, was zu einem ersten Wort von führt hello=hi.

Sobald die Variablenzuweisungen verarbeitet wurden, werden sie während der Befehlsausführung nicht erneut verarbeitet.

Siehe die Verarbeitung und ihre Ergebnisse unter set -x:

$ prefix=hello=hi
+ prefix=hello=hi
$ $prefix bash -c 'echo $hello'
+ hello=hi bash -c 'echo $hello'
-bash: hello=hi: command not found
$ hello=42 bash -c 'echo $hello'
+ hello=42
+ bash -c 'echo $hello'
42

evalBetrachten Sieenv für eine sicherere Variante der "Variablenerweiterung" als "Umgebungsvariablen" den Vorschlag von wjandrea :

prefix=hello=hi
env "$prefix" bash -c 'echo "$hello"'
hi

Es handelt sich nicht ausschließlich um eine Zuweisung von Befehlszeilenvariablen, da wir die envHauptfunktion des Dienstprogramms zum Zuweisen von Umgebungsvariablen zu einem Befehl verwenden, aber das gleiche Ziel erreicht. Die $prefixVariable wird während der Verarbeitung der Befehlszeile erweitert und gibt den Namen = Wert an env, an den sie weitergegeben wird bash.

Jeff Schaller
quelle
3
Ebenso $foo=bar bash -c ...funktioniert es nicht, da $foo=bares sich nicht um eine Variablenzuweisung handelt (unabhängig vom Wert von $foo), da $foo=bares nicht zum name=valueMuster für die Variablenzuweisung passt .
Muru
3

Weil $prefixes keine Aufgabe ist. @ Jeff hat die längere Erklärung .

Sie könnten stattdessen etwas Ähnliches mit einer Funktion tun:

$ prefix() { hello=hi "$@"; }
$ prefix bash -c 'echo "$hello"'
hi

... und Sie können diese sogar stapeln, wenn Sie möchten:

$ foo() { foo=123 "$@"; }
$ bar() { bar=456 "$@"; }
$ foo bar bash -c 'echo "$bar $foo"'
456 123
ilkkachu
quelle