Визначення змінної з експортом або без нього


955

Для чого export?

Яка різниця між:

export name=value

і

name=value

4
Дотично зауважте також, що export name=valueце не портативно. Залежно від того, що саме ви хочете, спробуйте name=value; export nameзнайти портативне рішення.
— tripleee

Відповіді:


1054

export робить змінну доступною для підпроцесів.

Це є,

export name=value

означає, що ім'я змінної доступне для будь-якого процесу, який ви запускаєте з цього процесу оболонки. Якщо ви хочете, щоб процес використовував цю змінну, використовуйте exportта запустіть процес із цієї оболонки.

name=value

означає, що змінна область обмежена оболонкою і не доступна для будь-якого іншого процесу. Ви б використовували це для (скажімо) змінних циклу, тимчасових змінних тощо.

Важливо зазначити, що експорт змінної не робить її доступною для батьківських процесів. Тобто, вказівка ​​та експорт змінної у спареному процесі не робить її доступною у процесі, який її запустив.


105
Зокрема, експорт робить змінну доступною для дочірніх процесів через навколишнє середовище.
— Beano

15
Я також додам, що якщо експорт знаходиться у файлі, який ви "джерелом" (наприклад, ім'я файлу), то він також експортує його у ваше робоче середовище.
— rogerdpack

6
@rogerdpack не можеш це зробити без експорту? кішка> бла \ на = привіт \ n. бла; ехо $ а; Виходи "привіт" для мене.
— David Winiecki

2
Приємно, що це працює навіть без експорту. Тому я думаю, що при пошуку файлу, якщо ви використовуєте експорт, це буде відображено в дочірніх процесах, якщо ви цього не зробите, це просто вплине на локальне середовище bash ...
— rogerdpack

19
У цьому є один крайній випадок; name=value command робить змінну доступною в підпроцесі command.
— Олівер Чарльворт

254

Щоб проілюструвати, що говорять інші відповіді:

$ foo="Hello, World"
$ echo $foo
Hello, World
$ bar="Goodbye"
$ export foo
$ bash
bash-3.2$ echo $foo
Hello, World
bash-3.2$ echo $bar

bash-3.2$ 

9
Ще один приклад для цьогоal$ foobar="Whatever" bash
— Алун

70

Інші відповіли, що експорт робить змінну доступною для підрозділів, і це правильно, але лише побічний ефект. Коли ви експортуєте змінну, вона розміщує цю змінну в середовищі поточної оболонки (тобто оболонки викликає putenv(3)або setenv(3)).
Середовище процесу успадковується через exec, що робить змінну видимою в підшах.

Правка (з перспективою 5 років): це дурна відповідь. Мета "експорту" полягає в тому, щоб змінні "опинилися в оточенні команд, що виконуються згодом", будь то ці команди підзагортами або підпроцесами. Наївною реалізацією було б просто помістити змінну в середовище оболонки, але це зробило б неможливим реалізацію export -p.


6
Зауважте, що це не зовсім вірно. В bashекспорті дійсно додати змінний в середу поточної оболонки, але це не той випадок з dash. Мені здається, що додавання змінної до середовища поточної оболонки є найпростішим способом реалізації семантики export, але така поведінка не є обов'язковою.
— Вільям Перселл

7
Я не впевнений, що dashстосується цього. Оригінальний плакат питав конкретно о bash.
— Морська зірка

14
Питання позначено тегами, bashале однаково стосується будь-якого варіанта бурно-оболонки. Бути надмірно конкретним та надавати відповіді, які стосуються лише bash- це велике зло.
— Вільям Перселл

12
bash- jQuery оболонки.
— Потерка

2
export makes the variable available to subshells, and that is correctЦе дуже заплутане використання термінології. Абонентам не потрібно exportуспадковувати змінні. Підпроцеси роблять.
— Аміт Найду

62

Було сказано, що не потрібно експортувати в баш при нерестування передпластів, в той час як інші кажуть, що навпаки. Важливо відзначити різницю між подоболочкі (ті, які створені (), ``, $()або петля) і підпроцеси (процеси, які викликаються по імені, наприклад , буквальне bashпоява в сценарії).

  • Суб- оболонки матимуть доступ до всіх змінних від батьківського, незалежно від стану експорту.
  • Суб процеси будуть тільки побачити експортовані змінні.

Що є спільним для цих двох конструкцій, це те, що жоден не може передати змінні назад до батьківської оболонки.

$ noexport=noexport; export export=export; (echo subshell: $noexport $export; subshell=subshell); bash -c 'echo subprocess: $noexport $export; subprocess=subprocess'; echo parent: $subshell $subprocess
subshell: noexport export
subprocess: export
parent:

Є ще одне джерело плутанини: деякі вважають, що "роздвоєні" підпроцеси - це ті, які не бачать неекспортованих змінних. Зазвичай fork () s одразу слідує за допомогою exec () s, і тому, здавалося б, слід шукати fork (), а насправді це exec (). Ви можете запускати команди без fork (), спочатку за допомогою execкоманди, і процеси, запущені цим методом, також не матимуть доступу до неекспортованих змінних:

$ noexport=noexport; export export=export; exec bash -c 'echo execd process: $noexport $export; execd=execd'; echo parent: $execd
execd process: export

Зауважте, що parent:цього разу ми не бачимо рядок, тому що ми замінили батьківську оболонку execкомандою, тому для виконання цієї команди нічого не залишається.


Я ніколи не бачив циклу, який (сам по собі) створив нижню оболонку; Інший конвеєр OTOH (завжди для частин, відмінних від останнього, іноді для останнього, залежно від вашої оболонки, версії та варіантів). Backgrounding ( &) також створює доподібну оболонку.
— dave_thompson_085

Що з цим var=asdf bash -c 'echo $var'чи var=asdf exec bash -c 'echo $var'? Вихід є asdf. ;Робить різницю , якщо помістити після визначення змінної. Яке було б пояснення? Схоже, що var(без ;) стосується породженого підпроцесу якось, оскільки оболонка походження не має нічого спільного з цим. echo $varнічого не друкує, якщо виконано у другому рядку. Але одна підкладка var=asdf bash -c 'echo $var'; echo $varдає asdf\nasdf.
— 4xy

31

export NAME=value для параметрів і змінних, які мають значення для підпроцесу.

NAME=value для тимчасових або циклічних змінних, приватних для поточного процесу оболонки.

Більш детально, exportпозначає ім'я змінної в середовищі, яке копіюється в підпроцеси та їх підпроцеси при створенні. Жодне ім’я або значення ніколи не копіюється з підпроцесу.

  • Поширена помилка - розмістити пробіл навколо знака рівності:

    $ export FOO = "bar"  
    bash: export: `=': not a valid identifier
  • BПідпроцес бачить лише експортовану змінну ( ):

    $ A="Alice"; export B="Bob"; echo "echo A is \$A. B is \$B" | bash
    A is . B is Bob
  • Зміни підпроцесу не змінюють основну оболонку:

    $ export B="Bob"; echo 'B="Banana"' | bash; echo $B
    Bob
  • Змінні, позначені для експорту, мають значення, скопійовані під час створення підпроцесу:

    $ export B="Bob"; echo '(sleep 30; echo "Subprocess 1 has B=$B")' | bash &
    [1] 3306
    $ B="Banana"; echo '(sleep 30; echo "Subprocess 2 has B=$B")' | bash 
    Subprocess 1 has B=Bob
    Subprocess 2 has B=Banana
    [1]+  Done         echo '(sleep 30; echo "Subprocess 1 has B=$B")' | bash
  • Тільки експортовані змінні стають частиною середовища ( man environ):

     $ ALICE="Alice"; export BOB="Bob"; env | grep "ALICE\|BOB"
     BOB=Bob

Отже, зараз має бути так ясно, як літнє сонце! Завдяки Мозгові Агнеу, алексупу та Вільяму Пруселлу.


12

export зробить змінну доступною для всіх оболонок, роздвоєних з поточної оболонки.


11

Слід зазначити, що ви можете експортувати змінну і пізніше змінити значення. Змінене значення змінної буде доступне для дочірніх процесів. Після встановлення експорту змінної, ви повинні зробити, export -n <var>щоб видалити властивість.

$ K=1
$ export K
$ K=2
$ bash -c 'echo ${K-unset}'
2
$ export -n K
$ bash -c 'echo ${K-unset}'
unset

Дякую, це саме та інформація, яку я шукав, тому що я побачив сценарій, який використовував змінні середовища, а потім "реекспортував" їх з новим значенням, і мені було цікаво, чи потрібно.
— Майк Ліпперт

8

Як ви вже могли знати, UNIX дозволяє процесам мати набір змінних оточуючих середовищ, які є ключовими / значеннями, і ключові, і значення - це рядки. Операційна система відповідає за збереження цих пар для кожного процесу окремо.

Програма може отримати доступ до змінних середовища через цей UNIX API:

  • char *getenv(const char *name);
  • int setenv(const char *name, const char *value, int override);
  • int unsetenv(const char *name);

Процеси також успадковують змінні середовища від батьківських процесів. Операційна система несе відповідальність за створення копії всіх "envars" в момент створення дочірнього процесу.

Bash , серед інших оболонок, здатний встановлювати свої змінні середовища на запит користувача. Це те, що exportіснує для.

export- команда Bash для встановлення змінної середовища для Bash. Усі змінні, встановлені за допомогою цієї команди, успадковуватимуться всіма процесами, які створив би цей Bash.

Детальніше про довкілля в Баші

Інший вид змінної в Bash - це внутрішня змінна. Оскільки Bash - це не просто інтерактивна оболонка, вона насправді є інтерпретатором сценарію, як і будь-який інший інтерпретатор (наприклад, Python), він здатний зберігати власний набір змінних. Слід зазначити, що Bash (на відміну від Python) підтримує лише рядкові змінні.

Позначення для визначення змінних Bash є name=value. Ці змінні залишаються всередині Bash і не мають нічого спільного зі змінними середовища, що зберігаються в операційній системі.

Детальніше про параметри оболонки (включаючи змінні)

Також варто зазначити, що відповідно до посібника Баша:

Навколишнє середовище для будь-якої простої команди чи функції може бути тимчасово доповнене, додавши її до присвоєння параметрів, як описано в параметрах оболонки . Ці заяви про присвоєння впливають лише на середовище, яке бачить ця команда.


Підводячи підсумки:

  • exportвикористовується для встановлення змінної середовища в операційній системі. Ця змінна буде доступна для всіх дочірніх процесів, створених поточним процесом Bash.
  • Позначення змінної Bash (ім'я = значення) використовується для встановлення локальних змінних, доступних лише для поточного процесу bash
  • Позначення змінної Bash з префіксом іншої команди створює змінну середовища лише для сфери дії цієї команди.

1
bash vars не підтримують стільки типів, як Python, але мають рядковий, цілий і два види масиву ("індексований" / традиційний та "асоціативний", схожий на масив awk, perl хеш чи диктант Python). Інші мушлі відрізняються; тільки рядок є портативним .
— dave_thompson_085

7

Загальноприйнятий відповідь має на увазі це, але я хотів би зробити явне з'єднання оболонки вбудованих команд:

Як уже згадувалося, exportзробить змінну доступною як для оболонки, так і для дітей. Якщо exportйого не використовувати, змінна буде доступна лише в оболонці, і лише вбудовані оболонки можуть отримати доступ до неї.

Це є,

tango=3
env | grep tango # prints nothing, since env is a child process
set | grep tango # prints tango=3 - "type set" shows `set` is a shell builtin

3

Ось ще один приклад:

VARTEST="value of VARTEST" 
#export VARTEST="value of VARTEST" 
sudo env | grep -i vartest 
sudo echo ${SUDO_USER} ${SUDO_UID}:${SUDO_GID} "${VARTEST}" 
sudo bash -c 'echo ${SUDO_USER} ${SUDO_UID}:${SUDO_GID} "${VARTEST}"'  

Тільки за допомогою експорту VARTEST значення VARTEST доступне у sudo bash -c '...'!

Для подальших прикладів див:


3

Два із творців UNIX, Брайан Керніган та Роб Пайк, пояснюють це у своїй книзі "Середовище програмування UNIX". Google за назвою, і ви легко знайдете версію PDF.

Вони звертаються до змінних оболонок у розділі 3.6 та зосереджуються на використанні exportкоманди в кінці цього розділу:

Коли ви хочете зробити значення змінної доступною в під оболонках, слід використовувати команду експорту оболонки. (Ви можете подумати про те, чому немає способу експортувати значення змінної з підколі в її батьківський).


2

Просто щоб показати різницю між експортованою змінною, що знаходиться в середовищі (env), і неекспортованою змінною, яка не знаходиться в середовищі:

Якщо я це роблю:

$ MYNAME=Fred
$ export OURNAME=Jim

тоді у env з'являється лише $ OURNAME. Змінна $ MYNAME відсутня в env.

$ env | grep NAME
OURNAME=Jim

але змінна $ MYNAME існує в оболонці

$ echo $MYNAME
Fred

1

За замовчуванням змінні, створені в сценарії, доступні лише для поточної оболонки; дочірні процеси (під оболонки) не матимуть доступу до встановлених або модифікованих значень. Дозвіл дочірніх процесів бачити значення вимагає використання команди експорту.


0

Хоча явно не зазначено в обговоренні, НЕ потрібно використовувати експорт під час нерестування підзаголовка з внутрішньої частини bash, оскільки всі змінні копіюються в дочірній процес.


Поясніть, будь ласка, так, як те, що ви говорите, здається, що прямо суперечить відповідям з прикладів вище
— Майк Ліпперт

Це правильний спосіб, якщо ви не хочете експортувати змінні в усьому світі, а доступні лише для підпроцесу! Дякую.
— jtblin
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.