Вопрос или проблема
Если я выполню
export TEST=foo
echo $TEST
Это выведет foo.
Если я выполню
TEST=foo echo $TEST
Это не сработает. Как я могу получить эту функциональность без использования export или скрипта?
Это происходит потому, что оболочка разворачивает переменную в командной строке до того, как фактически выполняется команда, и в это время переменная не существует. Если вы используете
TEST=foo; echo $TEST
это будет работать.
export
заставляет переменную появляться в окружении последовательно выполняемых команд (для того, как это работает в bash, смотрите help export
). Если вам нужно, чтобы переменная появилась в окружении одной команды, используйте то, что вы уже пробовали, т.е.
TEST=foo your-application
Синтаксис оболочки описывает это как функционально эквивалентное:
export TEST=foo
your-application
unset TEST
Смотрите спецификацию для деталей.
Интересная часть заключается в том, что команда export
переключает флаг экспорта для переменной name. Таким образом, если вы сделаете:
unset TEST
export TEST
TEST="foo"
TEST
будет экспортироваться, даже если она не была определена в момент, когда она была экспортирована. Однако дальнейший unset
должен удалить атрибут экспорта.
Я подозреваю, что вы хотите, чтобы переменные оболочки имели ограниченную область видимости, а не переменные окружения. Переменные окружения – это список строк, передаваемых командам при их выполнении.
В
var=value echo whatever
вы передаете строку var=value
окружению, которое получает echo. Однако echo
ничего не делает со своим списком окружения¹, и в любом случае в большинстве оболочек echo
встроен и, следовательно, не выполняется.
Если бы вы написали
var=value sh -c 'echo "$var"'
Это было бы другим делом. Здесь мы передаем var=value
команде sh
, и sh
действительно использует свое окружение. Оболочки преобразуют каждую² из полученных переменных из их окружения в переменную оболочки, поэтому переменная окружения var
, которую получает sh
, будет преобразована в переменную $var
, и когда она разворачивается в этой командной строке echo
, это станет echo value
. Поскольку окружение по умолчанию наследуется, echo
также получит var=value
в своем окружении (или получил бы, если бы он был выполнен), но, опять же, echo
не заботится о окружении.
Теперь, если, как я подозреваю, вы хотите ограничить область действия переменных оболочки, есть несколько возможных подходов.
Переносимо (Bourne и POSIX):
(var=value; echo "1: $var"); echo "2: $var"
(…) выше запускает подсистему (новый процесс оболочки в большинстве оболочек), так что любая переменная, объявленная там, будет влиять только на эту подсистему, поэтому я ожидаю, что приведенный выше код выведет “1: value” и “2: ” или “2: whatever-var-was-set-to-before”.
С большинством оболочек, похожих на Bourne (см. Список оболочек, которые поддерживают ключевое слово `local` для определения локальных переменных), вы можете использовать функции и встроенный “local”:
f() {
local var
var=value
echo "1: $var"
}
f
echo "2: $var"
С zsh вы можете использовать анонимные функции, которые, как и обычные функции, могут иметь локальную область видимости:
(){ local var=value; echo "1: $var"; }; echo "2: $var"
или:
function { local var=value; echo "1: $var"; }; echo "2: $var"
С bash и zsh (но не с ash, pdksh или AT&T ksh) этот трюк также работает:
var=value eval 'echo "1: $var"'; echo "2: $var"
Вариант, который работает в нескольких других оболочках (dash
, mksh
, yash
), но не в zsh
(если не в эмуляции sh
/ksh
):
var=value command eval 'echo "1: $var"'; echo "2: $var"
(используя command
перед специальной встроенной командой (здесь eval
) в оболочках POSIX убирает их особенность (здесь присвоения переменных перед ними остаются в силе после их возврата))
С оболочкой fish
вы можете делать переменные локальными для блока begin
..end
:
begin
set -l var value
echo 1: $var
end
echo 2: $var
С mksh
(и скоро zsh) вы можете использовать конструкцию ${|cmd}
, которая также может иметь локальную область видимости, убедившись, что она не разворачивается в ничего, следя за тем, чтобы вы не устанавливали $REPLY
внутри:
${|local var=value; echo "$var"}; echo "$var"
¹ Строго говоря, это не совсем верно. Несколько реализаций будут заботиться о локализационных переменных окружения (LANG
, LOCPATH
, LC_*
…), реализация GNU заботится о переменной окружения POSIXLY_CORRECT
(сравните env echo --version
с env POSIXLY_CORRECT=1 echo --version
на системе GNU).
² что касается только (некоторых из) тех, которые являются допустимыми именами переменных в синтаксисе данной оболочки. Например env ++=foo 1x=bar 3=qwe '#=rty' IFS=asd é=x sh -c 'код оболочки'
не создаст ++
, 1x
, 3
, #
переменные оболочки, так как это недопустимые имена переменных (хотя #
и 3
являются не переменными параметрами оболочки) и не импортирует IFS
из окружения, так как это могло бы быть источником уязвимостей в безопасности; что касается é
, YMMV.
Вы делаете это правильно, но синтаксис bash легко неправильно интерпретировать: вы могли бы подумать, что echo $TEST
заставляет echo
извлекать переменную окружения TEST
, а затем выводить ее, но это не так. Итак, задано
export TEST=123
тогда
TEST=456 echo $TEST
включает в себя следующую последовательность:
-
Оболочка разбирает всю командную строку и выполняет все подстановки переменных, так что командная строка превращается в
TEST=456 echo 123
-
Создаются временные переменные, установленные перед командой, поэтому сохраняется текущее значение
TEST
и перезаписывается 456; командная строка теперьecho 123
-
Выполняется оставшаяся команда, которая в данном случае выводит 123 на стандартный вывод (таким образом, оставшаяся команда оболочки даже не использует временное значение
TEST
) -
Восстанавливается значение
TEST
Используйте printenv вместо этого, так как это не включает в себя подстановку переменных:
>> export TEST=123
>> printenv TEST
123
>> TEST=456 printenv TEST
456
>> printenv TEST && TEST=456 printenv TEST && TEST=789 printenv TEST && printenv TEST
123
456
789
123
>>
Вы можете сделать это, используя:
TEST=foo && echo $TEST
Как вы уже обнаружили, в Bash, TEST=456 echo $TEST
не работает так, как вы ожидали, потому что вы обращаетесь к переменной в той же строке, которая в настоящее время интерпретируется оболочкой. Но будьте уверены, переменная была установлена на время текущей команды. Чтобы это проверить, вы могли бы сделать что-то вроде этого:
show () { echo "${!1}" ;}
TEST=456 show TEST
Ответ или решение
Чтобы установить переменную окружения в командной строке и использовать её в команде, необходимо учитывать особенности работы оболочки (шелла), такой как Bash. Когда вы пытаетесь выполнить команду следующего вида:
TEST=foo echo $TEST
вы не получите ожидаемого результата, поскольку оболочка сначала обрабатывает всю строку команды и выполняет подстановку переменных до того, как она начнёт выполнение самой команды. В данной ситуации, на момент выполнения команды echo
, переменная TEST
ещё не была присвоена, поэтому результат будет пустым.
Для того чтобы переменная TEST
была доступна в вашей командной строке, вы можете поступить следующим образом:
Установка переменной окружения
-
Экспорт переменной:
Используя командуexport
, вы можете установить переменную окружения, которая будет доступна для последующих команд.export TEST=foo echo $TEST
В этом случае вывод будет
foo
, поскольку переменная была экспортирована и теперь доступна для всех последующих команд в текущем сеансе. -
Локальное использование переменной:
Если вы хотите использовать переменную только в рамках одной команды, вы можете задать её непосредственно перед вызовом нужной команды:TEST=foo command
Это эквивалентно:
export TEST=foo command unset TEST
Важно отметить, что
command
получает доступ к переменнойTEST
, но после завершения выполнения команды переменная не сохраняется.
Для проверки значения переменной
Можно использовать различные подходы для проверки значения переменной после её задания:
-
Используйте вызов функции, чтобы получить значение переменной:
show() { echo "${!1}"; } TEST=456 show TEST
Запуск в подкоманде
Если вам нужно ограничить область видимости переменной и не хотите использовать export
, можете использовать подкоманду (субшелл):
(var=value; echo "1: $var"); echo "2: $var"
В этом примере первой команде будет доступна только переменная var
, созданная в первом блоке. После завершения исполнения подкоманды переменная var
не будет доступна во внешнем окружении.
Заключение
При работе с переменными окружения в оболочке Bash важно понимать порядок выполнения команд и подстановки переменных. Ключевые моменты, которые необходимо учитывать:
- Если хотите, чтобы переменная влияло на последующие команды, используйте
export
. - Для временного использования переменной в рамках одной команды, используйте синтаксис
VAR=value command
. - Поддерживайте чистоту своей среды, освобождая переменные после использования с помощью
unset
, если они больше не нужны.
Таким образом, правильное понимание и использование переменных в Bash позволяет более эффективно управлять окружением и переменными при работе в терминале.