Рассмотрим этот фрагмент:
$ 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 ...
и они работают? Что мне не хватает?
LD_PRELOAD
работа является то , что переменная установлена в программах окружающей среды - не в его командной строке.Ответы:
Вы видите ожидаемое поведение. Проблема в том, что родительская оболочка выполняет оценку
$SOMEVAR
в командной строке перед тем, как вызвать команду с измененной средой. Вам нужно получить оценку$SOMEVAR
отложенного до тех пор, пока среда не будет установлена.Ваши немедленные варианты включают:
SOMEVAR=BBB eval echo zzz '$SOMEVAR' zzz
.SOMEVAR=BBB sh -c 'echo zzz $SOMEVAR zzz'
.Оба они используют одинарные кавычки, чтобы предотвратить оценку родительской оболочки
$SOMEVAR
; он оценивается только после того, как он установлен в среде (временно, на время выполнения одной команды).Другой вариант - использовать нотацию суб-оболочки (как также предложил Маркус Кун в своем ответе ):
(SOMEVAR=BBB; echo zzz $SOMEVAR zzz)
Переменная устанавливается только во вспомогательной оболочке
источник
Возвращение к проблеме
Откровенно говоря, руководство по этому поводу сбивает с толку. В руководстве GNU Bash говорится:
Если вы действительно разбираете предложение, то в нем говорится, что среда для команды / функции изменена, но не среда для родительского процесса. Итак, это будет работать:
потому что среда для команды env была изменена перед ее выполнением. Однако это не сработает:
$ set -x; TESTVAR=bbb echo aaa $TESTVAR ccc + TESTVAR=bbb + echo aaa ccc aaa ccc
из-за того, что расширение параметра выполняется оболочкой.
Шаги переводчика
Другая часть проблемы заключается в том, что Bash определяет следующие шаги для своего интерпретатора:
Здесь происходит то, что встроенные команды не получают собственной среды выполнения, поэтому они никогда не видят измененную среду. Кроме того, простые команды (например, / bin / echo) действительно получают измененное окружение (поэтому пример env работал), но расширение оболочки происходит в текущем среде на шаге №4.
Другими словами, вы не передаете aaa $ TESTVAR ccc в / bin / echo; вы передаете интерполированную строку (развернутую в текущей среде) в / bin / echo. В этом случае, поскольку в текущей среде нет TESTVAR , вы просто передаете команде 'aaa ccc'.
Резюме
Документация могла бы быть намного яснее. Хорошо, что есть Stack Overflow!
Смотрите также
http://www.gnu.org/software/bash/manual/bashref.html#Command-Execution-Environment
источник
FOO=foo eval 'echo $FOO'
печатаетfoo
как положено. Это означает, что вы можете делать такие вещи, какIFS="..." read ...
.Чтобы добиться желаемого, используйте
( SOMEVAR=BBB; echo zzz $SOMEVAR zzz )
Причина:
Вы должны отделить присвоение от следующей команды точкой с запятой или новой строкой, в противном случае оно не будет выполнено до того, как произойдет расширение параметра для следующей команды (эхо).
Вам нужно сделать назначение внутри среды подоболочки , чтобы убедиться, что оно не сохраняется за пределами текущей строки.
Это решение короче, аккуратнее и эффективнее, чем некоторые из других предложенных, в частности, оно не создает новый процесс.
источник
(export SOMEVAR=BBB; python -c "from os import getenv; print getenv('SOMEVAR')")
SOMEVAR=BBB python -c "from os import getenv; print getenv('SOMEVAR')"
Причина в том, что это устанавливает переменную среды для одной строки. Но
echo
не делает расширение,bash
а делает. Следовательно, ваша переменная фактически раскрывается перед выполнением команды, даже если онаSOME_VAR
находитсяBBB
в контексте команды echo.Чтобы увидеть эффект, вы можете сделать что-то вроде:
$ SOME_VAR=BBB bash -c 'echo $SOME_VAR' BBB
Здесь переменная не раскрывается до тех пор, пока не будет выполнен дочерний процесс, поэтому вы увидите обновленное значение. если вы
SOME_VARIABLE
снова проверите в родительской оболочке, все останетсяAAA
, как и ожидалось.источник
SOMEVAR=BBB; echo zzz $SOMEVAR zzz
Использовать ; для разделения операторов, находящихся в одной строке.
источник
LD_PRELOAD
и что-то может работать перед исполняемым файлом без точки с запятой, хотя ... Еще раз большое спасибо - ура!Вот одна альтернатива:
SOMEVAR=BBB && echo zzz $SOMEVAR zzz
источник
&&
или;
разделяете их, назначение сохраняется, что не является желаемым поведением OP. У Маркуса Куна есть правильная версия этого ответа.