Другие отвечающие стороны помогли мне понять, что объем переменной оболочки о процессах и их потомках.
Когда Вы вводите команду как ls
на командной строке Вы на самом деле разветвляете процесс для выполнения ls
программа. Новый процесс имеет Вашу оболочку как своего родителя.
Любой процесс может иметь свои собственные "локальные" переменные, которые не передаются дочерним процессам. Это может также установить переменные "среды", которые являются. Используя export
создает переменную среды. В любом случае несвязанные процессы (коллеги оригинала) не будут видеть переменную; мы только управляем тем, что видят дочерние процессы.
Предположим, что у Вас есть оболочка удара, которую мы назовем A. Вы вводите bash
, который создает оболочку удара дочернего процесса, которую мы назовем B. Что-либо Вы звонили export
на в A будет все еще установлен в B.
Теперь, в B, Вы говорите FOO=b
. Одна из двух вещей произойдет:
FOO
, это создаст локальную переменную. Дети B не получат его (если B не будет звонить export
).FOO
, это изменит его для себя и его впоследствии разветвленных детей. Дети B будут видеть значение это присвоенный B. Однако это не будет влиять вообще.Вот быстрая демонстрация.
FOO=a # set "local" environment variable
echo $FOO # 'a'
bash # forks a child process for the new shell
echo $FOO # not set
exit # return to original shell
echo $FOO # still 'a'
export FOO # make FOO an environment variable
bash # fork a new "child" shell
echo $FOO # outputs 'a'
FOO=b # modifies environment (not local) variable
bash # fork "grandchild" shell
echo $FOO # outputs 'b'
exit # back to child shell
exit # back to original shell
echo $FOO # outputs 'a'
Все это объясняет мою исходную проблему: Я установил GEM_HOME
в моей оболочке, но когда я звонил bundle install
, это создало дочерний процесс. Поскольку я не использовал export
, дочерний процесс не получил оболочку GEM_HOME
.
Можно "не экспортировать" переменную - препятствуют тому, чтобы он был передан детям - при помощи export -n FOO
.
export FOO=a # Set environment variable
bash # fork a shell
echo $FOO # outputs 'a'
export -n FOO # remove environment var for children
bash # fork a shell
echo $FOO # Not set
exit # back up a level
echo $FOO # outputs 'a' - still a local variable
Процессы организованы как дерево: каждый процесс имеет уникального родителя, кроме init
который PID
всегда 1 и не имеет никакого родителя.
Создание нового процесса обычно идет через пару fork
/execv
системные вызовы, где среда дочернего процесса является копией родительского процесса.
Для помещения переменной в среду от оболочки, Вы имеете к export
та переменная, так, чтобы это было видимо рекурсивно всем детям. Но знайте, что, если ребенок изменяет значение переменной, измененное значение только видимо к нему и все процессы, созданные после того изменения (являющийся копией, как ранее сказано).
Возьмите также во внимание, что дочерний процесс мог изменить свою среду, например, мог сбросить его к значениям по умолчанию, как, вероятно, сделан от login
например.
FOO=bar
, это устанавливает значение для текущего процесса оболочки. Если я затем запускаю программу как (bundle install
), это создает дочерний процесс, который не получает доступ к FOO
. Но если я сказал export FOO=bar
, дочерний процесс (и его потомки) имел бы доступ к нему. Один из них мог, в свою очередь, звонить export FOO=buzz
изменить значение для его потомков, или просто FOO=buzz
изменить значение только для себя. Это о праве? А-ч
– Nathan Long
24.12.2011, 14:17
По крайней мере, под ksh
и bash
, переменные могут иметь три объема, не два как все остающиеся ответы в настоящее время говорят.
В дополнение к экспортируемому (т.е. среда) переменная и оболочка неэкспортируемые переменные объемы, существует также третий более узкий для функциональных локальных переменных.
Переменные, объявленные в оболочке, функционируют с typeset
маркер только видим в функциях, которыми они объявляются в и в (sub) функции, вызванные оттуда.
Это ksh
/ bash
код:
# Create a shell script named /tmp/show that displays the scoped variables values.
echo 'echo [$environment] [$shell] [$local]' > /tmp/show
chmod +x /tmp/show
# Function local variable declaration
function f
{
typeset local=three
echo "in function":
. /tmp/show
}
# Global variable declaration
export environment=one
# Unexported (i.e. local) variable declaration
shell=two
# Call the function that creates a function local variable and
# display all three variable values from inside the function
f
# Display the three values from outside the function
echo "in shell":
. /tmp/show
# Display the same values from a subshell
echo "in subshell":
/tmp/show
# Display the same values from a disconnected shell (simulated here by a clean environment start)
echo "in other shell"
env -i /tmp/show
производит этот вывод:
in function:
[one] [two] [three]
in shell:
[one] [two] []
in subshell:
[one] [] []
in other shell
[] [] []
Как Вы видите, экспортируемая переменная отображена от первых трех мест, неэкспортируемые переменные не отображен вне текущей оболочки, и функциональная локальная переменная не имеет никакого значения вне самой функции. Последний тест не показывает значений вообще, это вызвано тем, что экспортируемые переменные не совместно используются оболочками, т.е. они могут только быть наследованы, и наследованное значение не может быть затронуто впоследствии родительской оболочкой.
Обратите внимание, что это последнее поведение очень отличается от того Windows, где можно использовать Системные переменные, которые являются полностью глобальными и общими всеми процессами.
Лучшее объяснение, которое я могу найти об экспорте, является этим:
http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_02.html
Переменный набор в подоболочке или дочерней оболочке только видим к подоболочке, в которой это определяется. Экспортируемая Переменная на самом деле сделана быть переменной среды. Таким образом быть ясным Ваш bundle install
выполняет его собственную оболочку, которая не видит $GEM_HOME
если это не сделано environment
переменная иначе экспортирована.
Можно смотреть на документацию для переменного объема здесь:
FOO=bar
; необходимо использовать export
сделать его один. Вопрос исправлен соответственно.
– Nathan Long
24.12.2011, 14:35
Как и ожидалось, существует иерархия областей видимости переменных.
Внешняя область - это окружающая среда. Это единственная область, управляемая операционной системой, и поэтому гарантированно существует для каждого процесса. Когда процесс запускается, он получает копию своей родительской среды, после чего оба становятся независимыми: изменение дочерней среды не изменяет родительскую, а изменение родительской среды не меняет среду уже существующего дочернего.
Оболочки имеют собственное понятие переменных. Здесь все начинает немного запутываться.
Когда вы присваиваете значение переменной в оболочке, и эта переменная уже существует в среде, переменная среды получает новое значение. Однако, если переменная еще не находится в среде, она становится переменной оболочки . Переменные оболочки существуют только внутри процесса оболочки, аналогично тому, как переменные Ruby существуют только внутри сценария Ruby. Они никогда не наследуются дочерними процессами.
Здесь в игру вступает ключевое слово export
. Он копирует переменную оболочки в среду процесса оболочки, что позволяет дочерним процессам наследовать.
Локальные переменные - это переменные оболочки, привязанные к содержащим их блокам кода. Вы объявляете локальные переменные с помощью набора ключевого слова
(переносимый) или local
или объявления
(Bash). Как и другие переменные оболочки, локальные переменные не наследуются дочерними процессами. Также нельзя экспортировать локальные переменные.