Почему переменная видна в подоболочке?

18

В Learning Bash Book упоминается, что подоболочка будет наследовать только переменные среды, дескрипторы файлов и т. Д. И что она не будет наследовать переменные, которые не экспортируются:

$ var=15
$ (echo $var)
15
$ ./file # this file include the same command echo $var

$

Как я знаю, оболочка создаст две подоболочки для ()и для ./file, но почему в этом ()случае подоболочка идентифицирует varпеременную, хотя она не экспортируется, и в том ./fileслучае, если она не идентифицирует ее?

# Strace for () 
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25617
# Strace for ./file
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25631

Я попытался использовать, straceчтобы выяснить, как это происходит, и неожиданно обнаружил, что bash будет использовать одни и те же аргументы для системного вызова clone, так что это означает, что как разветвленный процесс, так ()и ./fileдолжен иметь одинаковое адресное пространство процесса родителя, так почему в ()случае, если переменная видима для подоболочки, и то же самое не происходит для ./fileслучая, хотя те же аргументы основаны на системном вызове clone?

user3718463
источник
vinc17 говорит true, даже если у вас есть pstree, когда у вас есть subshell, вы верите этому.
Персидский залив

Ответы:

15

Книга обучения Bash не так. Подоболочки наследуют все переменные. Четный $$(PID оригинальной оболочки) сохраняется. Причина в том, что для подоболочки оболочка просто разветвляется и не выполняет новую оболочку (наоборот, при вводе ./fileвыполняется новая команда, например, новая оболочка; в выводе strace смотрите execveи т. П.) , Так что, в основном, это просто копия (с некоторыми документированными отличиями).

Примечание: это не относится только к bash; это верно для любой оболочки.

vinc17
источник
Ладно, но сейчас я попытался запустить strace для оболочки и попытался выполнить ./file, но я не могу найти никакого вызова для exec, и поэтому адресное пространство должно быть одинаковым для обоих процессов, так как это можно объяснить?
user3718463
@ user3718463 Вы тоже использовали -fопцию straceдля отслеживания детей? Это необходимо, чтобы найти исполнителя.
vinc17
Да, я понял это, спасибо большое, я пропустил опцию -f, и поэтому я не могу найти вызов exec sys
user3718463
16

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

Некоторые конструкции оболочки приводят к тому, что оболочка разветвляется на дочерний процесс. Под Linux forkэто особый случай более общего cloneсистемного вызова, который вы наблюдали в straceжурнале. Ребенок запускает часть сценария оболочки. Дочерний процесс называется подоболочкой . Наиболее прямая такая конструкция command1 &: command1выполняется в подоболочке, а последующие команды выполняются в родительской оболочке. Другие конструкции, которые создают подоболочку, включают подстановку команд $(command2)и каналы command3 | command4( command3выполняется в подоболочке, command4в большинстве оболочек выполняется в подоболочке, но не в ksh или zsh).

Подоболочка - это копия родительского процесса, поэтому она имеет не только те же переменные среды, но и все те же внутренние определения: переменные (включая $$идентификатор процесса исходного процесса оболочки), функции, псевдонимы, параметры и т. Д. Перед выполнением кода в подоболочке bash устанавливает переменную BASHPIDв идентификатор процесса дочернего процесса.

Когда вы запускаете ./file, это выполняет внешнюю команду. Во-первых, оболочка разветвляет дочерний процесс; затем этот дочерний процесс выполняет (с помощью execveсистемного вызова) исполняемый файл ./file. Дочерний процесс наследует атрибуты процесса своих родителей: окружение, текущий каталог и т. Д. Внутренние аспекты приложения теряются при execveвызове: неэкспортированные переменные, функции и т. Д. Являются понятиями bash, о которых ядро ​​не знает, и они теряются, когда bash выполняет другую программу. Даже если эта другая программа оказывается сценарием bash, она выполняется новым экземпляром bash, который не знает или не заботится о том, что его родительский процесс также является экземпляром bash. Таким образом, переменная оболочки (неэкспортированная переменная) не сохраняется execve.

Жиль "ТАК - прекрати быть злым"
источник
Этот ответ прояснил мне несколько вещей. Единственное, что я не понимаю, это предложение во втором абзаце: «Ребенок выполняет часть сценария оболочки». На какой сценарий оболочки ссылаются?
flow2k
@ flow2k Сценарий (т.е. программа), который интерпретирует оболочка.
Жиль "ТАК - перестань быть злым"