Вопрос или проблема
Руководство по bash говорит: “Окружение для любой простой команды или функции может быть временно дополнено путем присвоения ей параметров с префиксом.”
Я также понимаю, что экспортируемые переменные по умолчанию передаются в подпроцессы, которые создает bash.
Я тестирую некоторые поведения с неэкспортируемыми переменными, и я не понимаю, как это работает, когда после присвоения следует оператор ‘;’, а затем выполняются другие команды.
Мой случай следующий:
VAR=hello; echo $VAR
печатает “hello”.
VAR=hello :; echo $VAR
печатает пустую строку.
VAR=hello; echo $VAR; bash -c 'echo $VAR'
печатает “hello” и пустую строку.
Вопрос в том, почему в первой и третьей командах VAR разворачивается? Это разные команды, без (по-видимому) присвоений параметров с префиксом.
PS: Я пробовал в bash 4.4.20 и 5.1.16, поведение одинаковое в обеих версиях.
Кажется, что вы в основном спрашиваете об экспорте и о том, когда что-то нужно экспортировать. Давайте рассмотрим каждый из ваших примеров:
-
VAR=hello; echo $VAR
: это две команды, выполняются в одной и той же сессии оболочки. Поскольку мы находимся в одной сессии, первое присвоение переменной достаточно, и нет необходимости ничегоэкспортировать
. -
VAR=hello :; echo $VAR
: здесь тоже у нас две команды в одной сессии оболочки, но в данном случае переменная была явно установлена только на время выполнения команды:
. Таким образом, вы не установили переменную в вашей сессии оболочки, вы установили ее только для этой одной команды. Поэтому, когда вы возвращаетесь в оболочку, переменная не задана, и echo печатает пустую строку. -
VAR=hello; echo $VAR; bash -c 'echo $VAR'
: здесь у нас одна сессия оболочки, в которой вы выполняетеVAR=hello
иecho $VAR
. Затем вы запускаете новую сессию оболочки, которая не наследует неэкспортируемые переменные, и в этой второй сессииecho $VAR
ничего не печатает. Здесь экспортирование могло бы сделать разницу:$ var=hello; bash -c 'echo $var' $ export var=hello; bash -c 'echo $var' hello
Ikkachu сделал очень хорошее замечание в удаленном сейчас комментарии, который я воспроизвожу ниже:
Хотя так как
:
— это специальная встроенная команда, назначение вVAR=hello :
должно быть по POSIX видно в следующей среде оболочки. Bash этого в обычном режиме не делает, но почти любая другая POSIX-совместимая оболочка сделает это, и bash в POSIX-режиме тоже. Так что$shell -c 'unset VAR; VAR=hello :; echo "$VAR"'
действительно печатаетhello
для таких оболочек, какbash -o posix
,dash
илиksh
. Это было бы иначе с, например,true
вместо:
, так как это простая команда.
Ответ или решение
Вопрос о странном расширении переменных оболочки в Bash требует глубокого понимания механизмов, которые управляют передачей переменных между сессиями оболочки и их воздействием на окружение команд. Давайте разберем это явление, используя методологию TEA, состоящую из Теории, Примеров и Применения.
Теория
В Bash переменные могут передаваться в среду выполнения команд через экспорт или одноразовое связывание с командой. Экспортированные переменные по умолчанию наследуются дочерними процессами, созданными Bash. Однако поведение локальных (неэкспортированных) переменных зависит от контекста их использования.
Основные положения:
- Экспорт переменных: Переменные, экспортированные командой
export
, становятся частью среды и доступны всем дочерним процессам. - Локальные переменные: Они видны только в текущей оболочке и не передаются в дочерние процессы, если не экспортированы.
- Присваивание переменных для команд: Переменные могут временно связываться с командой, если они предшествуют вызову этой команды. Это связывание лимитировано временем выполнения команды и не сохраняется после её завершения.
Примеры
Рассмотрим предоставленные случаи:
-
VAR=hello; echo $VAR
: Здесь две команды выполняются в одном сеансе оболочки. Переменная VAR присваивается значениюhello
, и это значение используется в командеecho
. Поскольку команды выполняются последовательно в одной и той же среде,VAR
доступен при выполненииecho
. -
VAR=hello :; echo $VAR
: В этом случае оператор:
— это встроенная команда, которая ничего не делает, но служит как точка выполнения. Переменная VAR временно присваивается этой команде. После завершения:
, VAR не сохраняется в сессии оболочки и поэтомуecho $VAR
выводит пустую строку. -
VAR=hello; echo $VAR; bash -c 'echo $VAR'
: Здесь снова есть последовательное выполнение команд.VAR=hello
иecho $VAR
выполняются в текущей сессии, гдеVAR
доступно, поэтомуecho
выводитhello
. Однакоbash -c 'echo $VAR'
запускает новую оболочку, где переменная VAR не экспортирована и, следовательно, недоступна, что приводит к выводу пустой строки.
Применение
Для эффективного управления переменными в скриптах Bash важно понимать принцип их наследования и видимости:
-
Экспортируйте переменные, если они должны быть доступны в дочерних процессах. Это особенно важно для сложных скриптов, чтобы поддерживать консистентность данных между различными частями программы.
-
Используйте временное присваивание, когда переменные нужны исключительно для конкретной команды, чтобы избегать загрязнения глобального пространства переменных.
-
Разрабатывайте скрипты с учётом окружения, в котором они выполняются. Например, включение POSIX-совместимого режима может изменить поведение относительных к именным переменным механизмов в Bash.
Важное замечание
Как было подсказано в удаленном комментарии на форуме Stack Exchange, :
является специальной встроенной командой, что означает, что присваивание переменных этой командой может быть видимо в последующей среде в других оболочках POSIX, таких как dash или ksh. Bash не ведёт себя таким образом в обычном режиме, но делает это в POSIX-совместимом режиме. Это важно учитывать при разработке скриптов, которые должны быть портативными и совместимыми с различными оболочками.
Таким образом, управление переменными в Bash — это вопрос понимания их области видимости и применения в контексте выполнения команд. Следуя описанным рекомендациям, можно обеспечить корректную работу скриптов и их предсказуемое поведение.