Dlaczego nie mogę używać zmiennych jako przedrostka polecenia do ustawiania zmiennych środowiskowych?

11

Zwykle możliwe jest ustawienie zmiennej środowiskowej dla polecenia poprzez prefiks:

hello=hi bash -c 'echo $hello'

Wiem również, że możemy użyć zmiennej, aby zastąpić dowolną część wywołania polecenia, taką jak:

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

Byłem bardzo zaskoczony, gdy dowiedziałem się, że nie można użyć zmiennej do prefiksu polecenia w celu ustawienia zmiennej środowiskowej. Przypadek testowy:

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

Dlaczego nie mogę ustawić zmiennej środowiskowej za pomocą zmiennej? Czy część przedrostka jest częścią specjalną? Udało mi się go uruchomić, używając eval z przodu, ale nadal nie rozumiem, dlaczego. Używam bash 4.4.

wbkang
źródło
1
Możesz użyć envzamiast tego eval, który IIRC jest bezpieczniejszy, ale wolniejszy.
wjandrea,

Odpowiedzi:

14

Podejrzewam, że jest to część sekwencji, która Cię łapie:

Słowa, które nie są przypisaniami ani przekierowaniami zmiennych, są rozwijane (patrz Rozszerzenia powłoki). Jeśli po rozwinięciu pozostaną jakieś słowa, pierwsze słowo jest uznawane za nazwę polecenia, a pozostałe słowa to argumenty

To pochodzi z podręcznika użytkownika Bash w rozdziale Prosta rozbudowa poleceń.

W tym cmd=bashprzykładzie nie ustawiono żadnych zmiennych środowiskowych, a bash przetwarza wiersz poleceń w górę poprzez rozwijanie parametrów, pozostawiając bash -c "echo hi".

W tym prefix=hello=hiprzykładzie ponownie nie ma przypisań zmiennych w pierwszym przejściu, więc przetwarzanie kontynuuje ekspansję parametrów, w wyniku czego powstaje pierwsze słowo hello=hi.

Po przetworzeniu przypisań zmiennych nie są one ponownie przetwarzane podczas wykonywania polecenia.

Zobacz przetwarzanie i jego wyniki w 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

Aby uzyskać bezpieczniejszą odmianę „zmiennych ekspansji” -as- „zmiennych środowiskowych” eval, rozważ sugestię wjandrea dotyczącąenv :

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

Nie jest to ściśle przypisanie zmiennych wiersza poleceń, ponieważ używamy envgłównej funkcji narzędzia do przypisywania zmiennych środowiskowych do polecenia, ale osiąga ten sam cel. $prefixZmienna jest rozszerzona podczas przetwarzania poleceń linii podanie nazwy = wartość env, która przekazuje je wraz z bash.

Jeff Schaller
źródło
3
Podobnie $foo=bar bash -c ...nie będzie działać, ponieważ $foo=barnie jest przypisaniem zmiennej (niezależnie od wartości $foo), ponieważ $foo=barnie pasuje do name=valuewzorca dla przypisania zmiennej.
muru,
3

Ponieważ $prefixto nie jest zadanie. @Jeff ma dłuższe wyjaśnienie .

Zamiast tego możesz zrobić coś podobnego z funkcją:

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

... i możesz je układać, jeśli chcesz:

$ foo() { foo=123 "$@"; }
$ bar() { bar=456 "$@"; }
$ foo bar bash -c 'echo "$bar $foo"'
456 123
ilkkachu
źródło