Які сфери застосування можуть мати змінні оболонки?


42

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

Я намагався використовувати bundle install, що є командою Ruby, яка використовує значення $GEM_HOMEдля своєї роботи. Я встановив $GEM_HOME, але команда ігнорувала це значення, поки не використовувалась export, як в export GEM_HOME=/some/path.

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

Крім того, враховуючи, що моє налаштування таких змінних застосовується лише до поточного сеансу оболонки, як би я встановив їх для, скажімо, демонізованого процесу?

Які сфери застосування можуть мати змінні оболонки?

Відповіді:


33

Процеси організовані як дерево: кожен процес має унікального батьківського, крім initякого PIDзавжди 1 і не має батьківського.

Створення нового процесу, як правило, відбувається через пару fork/ execvсистемних викликів, де середовище дочірнього процесу є копією батьківського процесу.

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

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


1
Ах! Добре, давайте подивимось, чи я це розумію. Якщо я скажу FOO=bar, в оболонці встановлено значення поточного процесу оболонки. Якщо я запускаю таку програму, як ( bundle install), це створює дочірній процес, до якого не можна отримати доступ FOO. Але якби я сказав export FOO=bar, дитячий процес (і його нащадки) мали б доступ до нього. Один із них міг, у свою чергу, закликати export FOO=buzzзмінити значення для своїх нащадків або просто FOO=buzzзмінити значення лише для себе. Це правда?
Натан Лонг

2
@NathanLong Це не зовсім так: у всіх сучасних оболонках змінна або експортується (і тому будь-яка зміна значення відображається в оточенні нащадків) або не експортується (це означає, що змінна не знаходиться в середовищі). Зокрема, якщо змінна вже знаходиться в середовищі при запуску оболонки, вона експортується.
Жил 'ТАК - перестань бути злим'

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

26

Принаймні, під kshі bash, змінні можуть мати три області, а не два, як усі відповіді, що залишилися.

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

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

Цей 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, де ви можете використовувати системні змінні, які є повністю глобальними та поділяють усі процеси.


12

Вони охоплюються методом

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

Коли ви вводите команду, як lsу командному рядку, ви фактично змушуєте процес запустити lsпрограму. Новий процес має свою оболонку як свого батьківського.

Будь-який процес може мати свої "локальні" змінні, які не передаються дочірнім процесам. Він також може встановити змінні "оточення", які є. Використання exportстворює змінну середовища. У будь-якому випадку непов'язані процеси (однорангові оригінали) не будуть бачити змінну; ми лише контролюємо те, що бачать дитячі процеси.

Припустимо, у вас є bash shell, який ми будемо називати A. Ви вводите bash, що створює дочірню обробку bash shell, яку ми будемо називати B. Все, що ви викликали exportв A, все одно буде встановлено у B.

Тепер, у В, ти кажеш FOO=b. Відбудеться одна з двох речей:

  • Якщо B не отримав (від A) змінну середовища, яку називають FOO, вона створить локальну змінну. Діти В не отримають його (якщо тільки Б не дзвонить export).
  • Якщо B було отримати (від А) змінне оточення callled 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

1
Коли ви говорите "це змінить це для себе та своїх дітей", ви повинні уточнити, що змінити значення можуть бачити лише діти, створені після модифікації.
enzotib

1
@enzotib - хороший момент. Оновлено.
Натан Лонг

3

Найкраще пояснення, яке я можу знайти щодо експорту, це:

http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_02.html

Набір змінних у межах підклітини або дочірньої оболонки видимий лише для нижньої частини, в якій вона визначена. Експортована змінна насправді робиться змінною середовища. Отже, щоб було зрозуміло, ваш bundle installвиконує власну оболонку, яка не бачить, $GEM_HOMEякщо не буде зроблена environmentзмінна ака, експортована.

Ви можете ознайомитись з документацією для змінної області тут:

http://www.tldp.org/LDP/abs/html/subshells.html


Так, я невірно використовував термін "змінна середовище" для FOO=bar; ви повинні використовувати його, exportщоб зробити його одним. Питання виправлено відповідно.
Натан Лонг

Перегляньте посилання, яке я додав.
Карлсон,

3

Існує ієрархія змінних областей, як очікувалося.

Середовище

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

Змінні оболонки

Оболонки мають власне поняття змінних. Тут дещо починає заплутатися.

Коли ви присвоюєте значення змінній в оболонці, а ця змінна вже існує в середовищі, змінна середовища отримує нове значення. Однак якщо змінної немає в середовищі, вона все ж стає змінною оболонки . Змінні оболонки існують лише в процесі оболонки, подібно до того, як змінні Ruby існують лише в сценарії Ruby. Вони ніколи не успадковуються дитячими процесами.

Ось де exportключове слово вступає в гру. Він копіює змінну оболонки в середовище процесу оболонки, що дозволяє успадковувати дочірні процеси.

Локальні змінні

Локальні змінні - це змінні оболонки, нанесені на кодові блоки, що містять їх. Ви оголошуєте локальні змінні за допомогою typesetключового слова (портативний) або localабо declare(Bash). Як і інші змінні оболонки, локальні змінні не успадковуються дочірніми процесами. Також не можна експортувати локальні змінні.

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