Почему я не могу использовать переменные в качестве префикса команды для установки переменных среды?

11

Как правило, для команды можно установить переменную среды, добавив к ней префикс следующим образом:

hello=hi bash -c 'echo $hello'

Я также знаю, что мы можем использовать переменную для замены любой части вызова команды, например:

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

Я был очень удивлен, узнав, что вы не можете использовать переменную для префикса команды для установки переменной среды. Прецедент:

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

Почему я не могу установить переменную среды, используя переменную? Префиксная часть - это особенная часть? Мне удалось заставить его работать, используя eval, но я до сих пор не понимаю, почему. Я использую Bash 4.4.

wbkang
источник
1
envВместо этого вы можете использовать eval, который IIRC является более безопасным, но медленнее.
wjandrea

Ответы:

14

Я подозреваю, что это часть последовательности, которая ловит вас:

Слова, которые не являются переменными назначениями или перенаправлениями, раскрываются (см. Расширения оболочки). Если после раскрытия остаются какие-либо слова, первое слово считается именем команды, а остальные слова - аргументами.

Это из справочного руководства по Bash в разделе «Простое расширение команд».

В этом cmd=bashпримере переменные окружения не установлены, и bash обрабатывает командную строку через расширение параметра, оставляя bash -c "echo hi".

В этом prefix=hello=hiпримере снова нет присвоений переменных на первом проходе, поэтому обработка продолжается до раскрытия параметра, в результате чего появляется первое слово hello=hi.

Как только назначения переменных были обработаны, они не обрабатываются повторно во время выполнения команды.

Смотрите обработку и ее результаты в разделе 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

Для более безопасной вариации «переменное расширение», как «переменные окружения» eval, рассмотрим предложение wjandreaenv :

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

Это не строго присваивание переменной командной строки, так как мы используем envосновную функцию утилиты для присвоения переменных среды команде, но она выполняет ту же цель. $prefixПеременный расширяются во время обработки командной строки, обеспечивая имя = значение env, который передает его вместе с bash.

Джефф Шаллер
источник
3
Точно так $foo=bar bash -c ...же не будет работать, потому что $foo=barне является присвоением переменной (каким бы ни было значение $foo), потому $foo=barчто не соответствует name=valueшаблону для назначения переменной.
Муру
3

Потому что $prefixэто не задание. У @Джеффа есть более длинное объяснение .

Вместо этого вы можете сделать то же самое с функцией:

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

... и вы можете даже сложить их, если хотите:

$ foo() { foo=123 "$@"; }
$ bar() { bar=456 "$@"; }
$ foo bar bash -c 'echo "$bar $foo"'
456 123
ilkkachu
источник