Bash: указание переменных среды для эха в командной строке?

Рассмотрим этот фрагмент:

$ SOMEVAR=AAA
$ echo zzz $SOMEVAR zzz
zzz AAA zzz

Здесь я установил $SOMEVAR в AAA в первой строке - и когда я повторяю его во второй строке, я получаю содержимое AAA, как и ожидалось.

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

$ SOMEVAR=BBB echo zzz $SOMEVAR zzz
zzz AAA zzz

... Я не получаю BBB, как и ожидалось, - получаю старое значение (AAA).

Так ли это должно быть? Если да, то как же тогда вы можете указать переменные типа LD_PRELOAD=/... program args ... и заставить его работать? Что мне не хватает?

Ответ 1

То, что вы видите, - это ожидаемое поведение. Проблема в том, что родительская оболочка оценивает $SOMEVAR в командной строке, прежде чем она вызывает команду с измененной средой. Вы должны получить оценку $SOMEVAR отложенной до тех пор, пока не будет установлена ​​среда.

Ваши непосредственные варианты:

  • SOMEVAR=BBB eval echo zzz '$SOMEVAR' zzz.
  • SOMEVAR=BBB sh -c 'echo zzz $SOMEVAR zzz'.

Оба они используют одинарные кавычки, чтобы предотвратить вычисление родительской оболочки $SOMEVAR; он оценивается только после того, как он установлен в среде (временно, на протяжении одной команды).

Другим вариантом является использование обозначения суб-оболочки (также предложенного Маркуса Куна в его ответе ):

(SOMEVAR=BBB; echo zzz $SOMEVAR zzz)

Переменная устанавливается только в под-оболочке

Ответ 2

Проблема, пересмотренная

Совершенно откровенно, руководство сбивает с толку по этому вопросу. В руководстве GNU Bash говорится:

Среда для любой простой команды или функции [обратите внимание, что это исключает встроенные функции] может быть временно добавлена, предварительно назначив ей назначения параметров, как описано в параметрах Shell. Эти утверждения присваивания влияют только на среду, видимую этой командой.

Если вы действительно разбираете предложение, то, по его словам, изменяется среда для команды/функции, но не среда для родительского процесса. Итак, это сработает:

$ TESTVAR=bbb env | fgrep TESTVAR
TESTVAR=bbb

потому что среда для команды env была изменена до ее выполнения. Однако это не сработает:

$ set -x; TESTVAR=bbb echo aaa $TESTVAR ccc
+ TESTVAR=bbb
+ echo aaa ccc
aaa ccc

из-за того, что расширение параметра выполняется оболочкой.

Шаги интерпретатора

Другая проблема заключается в том, что Bash определяет эти шаги для своего интерпретатора:

  • Считывает свой ввод из файла (см. "Сценарии оболочки" ) из строки в качестве аргумента для параметра -c invocation (см. Invoking Bash) или из пользовательского терминала.
  • Прерывает ввод в слова и операторы, подчиняясь правилам цитирования описанных в Quoting. Эти маркеры разделяются метасимволами. На этом этапе выполняется расширение псевдонима (см. Псевдонимы).
  • Разбирает токены в простые и составные команды (см. раздел "Команды оболочки" ).
  • Выполняет различные расширения оболочки (см. раздел Расширения оболочки), разбиение расширенных токенов на списки имен файлов (см. имя файла Расширение), а также команды и аргументы.
  • Выполняет любые необходимые перенаправления (см. раздел "Перенаправления" ) и удаляет операторы перенаправления и их операнды из списка аргументов.
  • Выполняет команду (см. Выполнение команд).
  • Необязательно ждет завершения команды и сбора ее выхода status (см. "Выход из состояния" ).

Что здесь происходит, так это то, что встроенные пользователи не получают свою собственную среду исполнения, поэтому они никогда не видят измененную среду. Кроме того, простые команды (например,/bin/echo) действительно получают модифицированную ennvironment (именно поэтому работал пример env), но расширение оболочки происходит в текущей среде на шаге 4.

Другими словами, вы не передаете 'aaa $TESTVAR ccc' в /bin/echo; вы передаете интерполированную строку (как расширенную в текущей среде) в /bin/echo. В этом случае, поскольку в текущей среде нет TESTVAR, вы просто передаете команду "aaa ccc" в команду.

Резюме

Документация может быть намного понятнее. Хорошо, что там переполнение стека!

См. также

http://www.gnu.org/software/bash/manual/bashref.html#Command-Execution-Environment

Ответ 3

Чтобы достичь желаемого, используйте

( SOMEVAR=BBB; echo zzz $SOMEVAR zzz )

Причина:

  • Вы должны отделить назначение точкой с запятой или новой строкой от следующей команды, иначе она не будет выполнена до того, как произойдет расширение параметра для следующей команды (эхо).

  • Вам нужно сделать назначение внутри среды подсетей, чтобы убедиться, что оно не сохраняется за пределами текущей строки.

Это решение более короткое, более аккуратное и более эффективное, чем некоторые другие, в частности, оно не создает новый процесс.

Ответ 4

Причина в том, что это устанавливает переменную среды для одной строки. Но echo не выполняет расширение, bash делает. Следовательно, ваша переменная фактически расширяется до выполнения команды, даже если SOME_VAR есть BBB в контексте команды echo.

Чтобы увидеть эффект, вы можете сделать что-то вроде:

$ SOME_VAR=BBB bash -c 'echo $SOME_VAR'
BBB

Здесь переменная не разворачивается до тех пор, пока не завершится дочерний процесс, поэтому вы увидите обновленное значение. если вы снова проверите SOME_VARIABLE в родительской оболочке, все равно AAA, как и ожидалось.

Ответ 5

SOMEVAR=BBB; echo zzz $SOMEVAR zzz

Использовать; для разделения операторов, которые находятся на одной строке.

Ответ 6

Здесь одна альтернатива:

SOMEVAR=BBB && echo zzz $SOMEVAR zzz