Какая разница в использовании между переменными оболочки и переменными среды?

16

Я на самом деле не знал, что есть два разных типа переменных, к которым я могу получить доступ из командной строки. Все, что я знал, это то, что я могу объявить переменные, такие как:

foo="my dear friends"
bar[0]="one"
bar[1]="two"
bar[2]="three"

или доступ к ним со знаком $, например:

echo $foo
echo ${bar[1]}

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

echo $PWD
PATH=$PATH:"/usr/bin/myProg"

Теперь я слышал, что есть два (по крайней мере?) Типа переменных: переменные оболочки и переменные окружения.

  • Какова цель иметь два разных типа?
  • Как я знаю, какой тип переменной?
  • Каковы типичные обычаи для каждого?
sharkant
источник

Ответы:

14

Переменные среды представляют собой список name=valueпар, которые существуют независимо от того, какой программой является программа (оболочка, приложение, демон…). Как правило, они наследуются дочерними процессами (созданными последовательностью fork/ exec): дочерние процессы получают свою собственную копию родительских переменных.

Переменные оболочки существуют только в контексте оболочки. Они наследуются только в подоболочках (т. Е. Когда оболочка разветвляется без execоперации). В зависимости от свойств оболочки переменные могут быть не только простыми строками, такими как окружения, но также массивами, составными, типизированными переменными, такими как целые числа или числа с плавающей запятой и т. Д.

Когда оболочка запускается, все переменные среды, которые она наследует от своего родителя, становятся также переменными оболочки (если они не являются недопустимыми в качестве переменных оболочки и других угловых случаев, например, IFSсбрасываемых некоторыми оболочками), но эти унаследованные переменные помечаются как экспортированные 1 . Это означает, что они останутся доступными для дочерних процессов с потенциально обновленным значением, установленным оболочкой. Это также относится к переменным, созданным под оболочкой и помеченным как экспортированные с помощью exportключевого слова.

Массив и другие переменные сложного типа не могут быть экспортированы, если их имя и значение не могут быть преобразованы в name=valueшаблон или если существует специальный механизм оболочки (например: bashэкспортирует функции в среде и некоторые экзотические, не POSIX-оболочки, такие как rcи esмогут экспортировать массивы ).

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

Также обратите внимание, что современные оболочки (как минимум kshи bash) поддерживают третью область видимости переменных оболочки. Переменные, созданные в функциях с typesetключевым словом, являются локальными для этой функции (способ, которым объявляется функция, включает / отключает эту функцию ksh, и поведение сохранения отличается между bashи ksh). См. Https://unix.stackexchange.com/a/28349/2594.

1 Это относится и к современной скорлупе нравится ksh, dash, bashи подобная. Унаследованная оболочка Bourne и оболочки синтаксиса, отличные от Bourne, cshимеют различное поведение.

jlliagre
источник
1
Все наследуется дочерними процессами, так как дочерние элементы создаются как ответвление (точная копия) их родителя. Суть переменных среды заключается в том, что они передаются execve()системному вызову, поэтому (как правило) используются для сохранения данных при выполнении других команд (в том же процессе).
Стефан
Не все переменные среды переводятся в переменные оболочки. Только те, которые допустимы в качестве имени переменной оболочки (и с некоторыми исключениями, как IFSв некоторых оболочках).
Стефан
Такие оболочки, как rc, esмогут экспортировать массивы с использованием adhoc-кодировки. bashа rcтакже может экспортировать функции, используя переменные среды (опять же, используя специальную кодировку).
Стефан
В ksh93, typesetограничивает сферу только функции , объявленные с function foo { ...; }синтаксисом, а не с Борна ( foo() cmdсинтаксис) (и это статическое обзорного не является динамическим , как и в других оболочках).
Стефан
@ StéphaneChazelas Спасибо за обзор! Ответ обновлен, чтобы учесть ваши замечания.
Jlliagre
17

Переменные оболочки

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

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

var="hello"

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

Переменные среды

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

VAR="hello"  # shell variable created
export VAR   # variable now part of the environment

или

export VAR="hello"

После экспорта переменной оболочки она остается экспортированной до тех пор, пока она не будет сброшена или пока ее «свойство экспорта» не будет удалено (с помощью export -nin bash), поэтому обычно нет необходимости повторно экспортировать ее. Сброс переменной с помощью unsetудаляет ее (независимо от того, является ли она переменной среды или нет).

Массивы и ассоциативные хеши в bash и другие оболочки нельзя экспортировать, чтобы они стали переменными среды. Переменные среды должны быть простыми переменными, значения которых являются строками, и они часто имеют имена, состоящие из заглавных букв.

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

Коллекция переменных среды в процессе часто упоминается как «среда процесса». Каждый процесс имеет свою собственную среду.

Переменные среды могут быть только «перенаправлены», то есть дочерний процесс никогда не может изменять переменные среды в своем родительском процессе, и кроме настройки среды для дочернего процесса при его запуске, родительский процесс может не изменять существующую среду дочерний процесс.

Переменные среды могут быть перечислены с env(без каких-либо аргументов). Кроме этого, они выглядят так же, как неэкспортированные переменные оболочки в сеансе оболочки. Это немного особенное для оболочки, так как большинство других языков программирования обычно не смешивают «обычные» переменные с переменными среды (см. Ниже).

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

env CC=clang CXX=clang++ make

Это начинается makeс переменной среды, CCустановленной в значение clangи CXXустановленной в clang++.

Он также может быть использован для очистки среды для процесса:

env -i bash

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

Пример различия

$ var="hello"   # create shell variable "var"
$ bash          # start _new_ bash session
$ echo "$var"   # no output
$ exit          # back to original shell session
$ echo "$var"   # "hello" is outputted
$ unset var     # remove variable

$ export VAR="hello"  # create environment variable "VAR"
$ bash
$ echo "$VAR"         # "hello" is outputted since it's exported
$ exit                # back to original shell session
$ unset VAR           # remove variable

$ ( export VAR="hello"; echo "$VAR" )  # set env. var "VAR" to "hello" in subshell and echo it
$ echo "$VAR"         # no output since a subshell has its own environment

Другие языки

В большинстве языков программирования есть библиотечные функции, которые позволяют получать и устанавливать переменные среды. Обратите внимание, что поскольку переменные среды хранятся в виде простых отношений ключ-значение, они обычно не являются «переменными» языка. Программа может извлечь значение (которое всегда является символьной строкой), соответствующее ключу (имя переменной среды), но затем должно будет преобразовать его в целое число или любой другой тип данных, который язык ожидает получить от значения.

В C, переменные окружения могут быть доступны с помощью getenv(), setenv(), putenv()и unsetenv(). Переменные, созданные с помощью этих подпрограмм, наследуются таким же образом любым процессом, который запускает программа на Си.

Другие языки могут иметь специальные структуры данных для выполнения той же задачи, такие как %ENVхэш в Perl или ENVIRONассоциативный массив в большинстве реализаций awk.

Кусалананда
источник
THX, блестяще четкое объяснение. Таким образом, среда похожа на большое поле, в котором другие программы могут жить и видеть каждую из переменных среды. Некоторые программы имеют свои закрытые переменные, только они сами могут их видеть, как оболочка. но есть механизм, позволяющий сделать закрытые переменные видимыми для всех, называемые «экспорт». Если это хорошо понято, то единственное, в чем я не уверен, может ли существовать более чем одна среда одновременно?
sharkant
@sharkant Каждый запущенный процесс имеет свою собственную среду. Эта среда наследуется от процесса, который ее запустил. Между средами разных процессов никогда не бывает «перекрестных разговоров». Единственный способ изменить переменную среды в процессе - это изменить сам процесс.
Кусалананда
спасибо за разъяснение моего понимания. Каждая рыба внутри своего аквариума. Как насчет процессов, которые порождают другие процессы? Все ли процессы и их дочерние процессы находятся внутри одной среды или у каждого своя?
sharkant
1
@sharkant В большинстве языков есть библиотечные функции, которые позволяют получать и устанавливать переменные окружения. В C, это делается с getenv(), setenv(), putenv()и unsetenv(). Переменные, созданные с помощью этих подпрограмм, наследуются таким же образом любым процессом, который запускает программа на Си. Другие языки могут иметь специальные структуры данных для той же вещи, как %ENVв Perl.
Кусалананда
1
FWIW: exec*()семейство функций также может устанавливать среду для выполняемого процесса.
Сать Кацура
5

Переменные оболочки трудно дублировать.

$ FOO=bar
$ FOO=zot
$ echo $FOO
zot
$ 

Однако переменные среды могут быть дублированы; это просто список, и в списке могут быть повторяющиеся записи. Вот envdup.cдля этого.

#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

extern char **environ;

int main(int argc, char *argv[]) {
    char **newenv;
    int envcount = 0;

    if (argc < 2) errx(64, "Usage: envdup command [args ..]");

    newenv = environ;
    while (*newenv++ != NULL) envcount++;

    newenv = malloc(sizeof(char *) * (envcount + 3));
    if (newenv == NULL) err(1, "malloc failed");
    memcpy(newenv, environ, sizeof(char *) * envcount);
    newenv[envcount]   = "FOO=bar";
    newenv[envcount+1] = "FOO=zot";
    newenv[envcount+2] = NULL;

    environ = newenv;
    argv++;
    execvp(*argv, argv);
    err(1, "exec failed '%s'", *argv);
}

Который мы можем скомпилировать и запустить, сказав envdupзатем запустить, envчтобы показать нам, какие переменные среды установлены ...

$ make envdup
cc     envdup.c   -o envdup
$ unset FOO
$ ./envdup env | grep FOO
FOO=bar
FOO=zot
$ 

Возможно, это полезно только для поиска ошибок или других странностей в том, насколько хорошо программы работают **environ.

$ unset FOO
$ ./envdup perl -e 'exec "env"' | grep FOO
FOO=bar
$ ./envdup python3 -c 'import os;os.execvp("env",["env"])' | grep FOO
FOO=bar
FOO=zot
$ 

Похоже, Python 3.6 здесь слепо передает дубликаты (дырявая абстракция), а Perl 5.24 - нет. Как насчет снарядов?

$ ./envdup bash -c 'echo $FOO; exec env' | egrep 'bar|zot'
zot
FOO=zot
$ ./envdup zsh -c 'echo $FOO; exec env' | egrep 'bar|zot' 
bar
FOO=bar
$ 

Черт возьми, что произойдет, если sudoочистить только первую запись среды, а затем bashзапустить со второй? Привет PATHили LD_RUN_PATHподвиг. Ваш sudoвсе остальное ?) Исправлено для этой дыры ? Эксплойты по безопасности не являются ни «случайным отличием», ни просто «ошибкой» в вызывающей программе.

thrig
источник
1
Это правда, но различие в отдельных случаях и, возможно, ошибка программы, устанавливающей дублирующую переменную.
jlliagre
1
См. Rt.perl.org/Public/Bug/Display.html?id=127158 (CVE-2016-2381)
Стефан
0

Переменная окружения похожа на переменную оболочки , но это не является специфичным для оболочки . Все процессы в системах Unix имеют хранилище переменных среды . Основное различие между переменными среды и оболочки состоит в том, что операционная система передает все переменные среды вашей оболочки программам, которые запускает оболочка, тогда как переменные оболочки не могут быть доступны в командах, которые вы запускаете.

env –Команда позволяет запускать другую программу в пользовательской среде без изменения текущей. При использовании без аргумента он напечатает список текущих переменных окружения. printenv –Команда печатает все или указанные переменные среды. set –Команда устанавливает или отменяет переменные оболочки. При использовании без аргумента он напечатает список всех переменных, включая переменные окружения и оболочки, а также функции оболочки. unset –Команда удаляет переменные оболочки и среды. export –Команда устанавливает переменные среды

Мехди Селлами
источник