Разница между “a=b” и “export a=b” в bash

Вопрос или проблема

В чем разница между:

a=b

и

export a=b

в bash?

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

export распространяет переменную на подпроцессы.

Например, если вы сделали

FOO=bar

тогда подпроцесс, который проверяет на наличие FOO, не найдет переменную, тогда как

export FOO=bar

позволит подпроцессу найти ее.

Но если FOO уже была определена как переменная окружения, то FOO=bar изменит значение этой переменной окружения.

Например:

FOO=one     # Не переменная окружения
export FOO  # Теперь FOO - переменная окружения
FOO=two     # Обновляет переменную окружения, так что подпроцессы увидят $FOO = "two"

Старые оболочки не поддерживали синтаксис export FOO=bar; необходимо было написать FOO=bar; export FOO.

Если вы не используете export, вы не определяете переменную окружения; вы создаете только переменную оболочки.

Переменные оболочки доступны только процессу оболочки; переменные окружения доступны любому последующему процессу, не только оболочкам.

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

Файл a.ksh –

#!/bin/ksh
FOO=bar

В командной строке выполните это

> . a.ksh

Это выполнит команды в той же оболочке, и $FOO будет доступен.

В то время как,

> a.ksh

Сделает $FOO доступной только внутри a.ksh, после вызова a.ksh она не будет существовать.

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

В последнем случае оба выражения строго эквивалентны.

Скрипт для демонстрации эффекта export (назовем его export-demo.sh):

#!/bin/bash

# Только определение функции, здесь еще нет выполняемого кода
function some_function() {
  echo "  function:var: $var"
  #local var
  var="Set in function"
  echo "  function:var: $var"
}

# Определяем переменную
var="Set in top script"
#export var

echo script:var: $var

# Это под-оболочка
(
  echo "  sub-shell:var: $var"
  var="Set in sub-shell"
  echo "  sub-shell:var: $var"
)

echo script:var: $var

# Это под-процесс (может быть любой исполняемый файл)
bash -c '
  echo "  sub-process:var: $var"
  var="Set in sub-process"
  echo "  sub-process:var: $var"
'

# Вызов функции этого скрипта
echo script:var: $var
some_function
echo script:var: $var

Выполнение без export (строка закомментирована) дает следующий вывод:

$ bash export-demo.sh; echo shell: $var
script: Set in top script
  sub-shell: Set in top script
  sub-shell: Set in sub-shell
script: Set in top script
  sub-process: 
  sub-process: Set in sub-process
script: Set in top script
  function: Set in top script
  function: Set in function
script: Set in function
shell:

Как мы видим, переменная, определенная на уровне скрипта:

  • передается в под-оболочку, но изменения не отражаются на уровне скрипта. Это потому, что под-оболочка получает копию переменной скрипта верхнего уровня;
  • передается в функцию и может быть изменена оттуда (потому что переменная функции не определена как local, см. ниже);
  • не передается в под-процесс.

Разкомментируем строку export var и запустим скрипт снова:

$ bash export-demo.sh; echo shell: $var
script:var: Set in top script
  sub-shell:var: Set in top script
  sub-shell:var: Set in sub-shell
script:var: Set in top script
  sub-process:var: Set in top script
  sub-process:var: Set in sub-process
script:var: Set in top script
  function:var: Set in top script
  function:var: Set in function
script:var: Set in function
shell:

Вот что делает export: он экспортирует переменную оболочки в качестве переменной окружения для под-процессов, которые может породить скрипт. Изменения из под-процесса не распространяются на верхний скрипт (потому что также получают копию исходной переменной).

Обратите внимание, что изменений в верхнюю оболочку не вносится (последняя строка вывода). Это потому, что скрипт выполняется из оболочки как под-процесс, даже если он вызван как:

$ chmod +x export-demo.sh; ./export-demo.sh

(Единственный способ избавиться от выполнения скрипта как под-процесса — это загрузить его в текущую сессию оболочки, как указано в этом ответе.)

Бонус. Разкомментируем строку local var и запустим скрипт снова:

$ bash export-demo.sh; echo shell: $var
script:var: Set in top script
  sub-shell:var: Set in top script
  sub-shell:var: Set in sub-shell
script:var: Set in top script
  sub-process:var: Set in top script
  sub-process:var: Set in sub-process
script:var: Set in top script
  function:var: Set in top script
  function:var: Set in function
script:var: Set in top script
shell:

Как мы видим, переменная, определенная как local в функции, получает копию от верхнего скрипта и не может изменить значение переменной верхнего скрипта.

Ответ или решение

Различие между командами a=b и export a=b в Bash-среде является основополагающим для понимания того, как обрабатываются переменные внутри оболочки.

Теория

Понимание отличается тем, является ли переменная локальной для текущей оболочки или же она доступна также для дочерних процессов. Когда вы используете a=b, вы создаете или изменяете значение переменной a только в контексте текущей сессии оболочки, называемого также shell-variable или локальной переменной. Такие переменные видны только внутри текущей оболочки и не передаются дочерним процессам.

С другой стороны, команда export a=b делает переменную a доступной также для любых процессов, которые оболочка может в дальнейшем породить, называемые environment-variable или переменные окружения. Экспорт переменной означает, что она будет включена в список переменных среды, передаваемых дочернему процессу при его запуске.

Пример

Рассмотрим следующий пример, чтобы закрепить теорию:

#!/bin/bash
a=b       # Установка локальной переменной
export a  # Изменение переменной на переменную окружения

Если мы запускаем скрипт, содержащий только первую строку a=b, затем пытаемся получить доступ к переменной a в дочернем процессе (например, через новый вызов bash), то дочерний процесс не увидит эту переменную, так как она не экспортирована.

#!/bin/bash
a=b
bash -c 'echo $a' # В этой точке подскрипт не увидит 'a'

Выход будет пустым, потому что дочерний процесс не видит локальные переменные родительской оболочки, если они не экспортированы.

Однако, если мы добавим export:

#!/bin/bash
a=b
export a
bash -c 'echo $a' # Теперь подскрипт увидит 'a' и напечатает 'b'

Теперь вывод будет b — дочерний процесс видит значение переменной, так как оно было экспортировано в ее окружение.

Применение

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

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

Вот пример более сложного сценария, демонстрирующего использование переменных окружения:

#!/bin/bash

build_project() {
  echo "Building project in $PROJECT_DIR..."
  # Допустим, здесь были бы команды сборки
}

PROJECT_DIR="/path/to/project"
export PROJECT_DIR

# Вызов функции, которая может использовать PROJECT_DIR
build_project

# Запуск процесса, который также использует PROJECT_DIR
(cd /other/path && bash -c 'echo "The current project directory is $PROJECT_DIR"')

В этом примере переменная PROJECT_DIR экспортируется, чтобы быть доступной и внутри функции build_project, и в поднятой оболочке команды bash -c, показывая, как переменные окружения могут предоставлять глобальный контекст для сложной настройки скрипта.

Таким образом, понимание различий между локальными переменными оболочки и экспортированными переменными окружения является критически важным для эффективного скриптинга и управления процессами в Unix-подобных системах.

Оцените материал
Добавить комментарий

Капча загружается...