Действительно ли скобки помещают команду в подоболочку?

94

Из того, что я прочитал, размещение команды в скобках должно запускать ее в подоболочке, аналогично запуску скрипта. Если это правда, как он видит переменную x, если x не экспортируется?

x=1

Запуск (echo $x)в командной строке приводит к 1

Запуск echo $xв скрипте ничего не дает, как и ожидалось

Igorio
источник

Ответы:

134

Подоболочка начинается с почти идентичной копии исходного процесса оболочки. Под капотом оболочка вызывает forkсистемный вызов 1 , который создает новый процесс, чей код и память являются копиями 2 . Когда подоболочка создана, между ней и ее родителем очень мало различий. В частности, они имеют одинаковые переменные. Даже $$специальная переменная сохраняет то же значение в подоболочках: это идентификатор процесса исходной оболочки. Точно так же $PPIDPID родителя оригинальной оболочки.

Несколько оболочек изменяют несколько переменных в подоболочке. Bash устанавливает BASHPIDPID процесса оболочки, который изменяется в подоболочках. Bash, zsh и mksh обеспечивают $RANDOMполучение разных значений в родительском и подоболочке. Но кроме встроенных особых случаев, подобных этим, все переменные в подоболочке имеют то же значение, что и в исходной оболочке, то же состояние экспорта, то же состояние только для чтения и т. Д. Все определения функций, определения псевдонимов, параметры оболочки и другие настройки также наследуются.

У подоболочки, созданной с помощью (…)тех же файловых дескрипторов, что и у ее создателя. Некоторые другие средства создания подоболочек изменяют некоторые файловые дескрипторы перед выполнением пользовательского кода; например, левая сторона трубы проходит в подоболочке 3 со стандартным выходом, подключенным к трубе. Подоболочка также начинается с того же текущего каталога, с той же маской сигналов и т. Д. Одним из немногих исключений является то, что подоболочки не наследуют пользовательские прерывания: игнорируемые сигналы ( ) остаются игнорируемыми в подоболочке, но другие прерывания ( SIGNAL ) сбрасываются к действию по умолчанию 4 .trap '' SIGNALtrap CODE

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

Таким образом:

x=1
(echo $x)

печатает, 1потому что подоболочка является копией оболочки, которая ее породила.

x=1
sh -c 'echo $x'

иногда запускается оболочка как дочерний процесс оболочки, но xвторая строка не имеет больше связи со xвторой строкой, чем

x=1
perl -le 'print $x'

или же

x=1
python -c 'print x'

1 Исключением является ksh93оболочка, в которой разветвление оптимизировано, и большинство его побочных эффектов эмулируется.
2 Семантически, это копии. С точки зрения реализации, происходит много обмена.
3 Для правой стороны это зависит от корпуса.
4 Если вы проверите это, обратите внимание, что подобные вещи$(trap) могут сообщать о ловушках оригинальной оболочки. Также обратите внимание, что во многих снарядах в угловых случаях есть ошибки, связанные с ловушками. Например, ninjalj отмечает, что в bash 4.3 bash -x -c 'trap "echo ERR at \$BASH_SUBSHELL \$BASHPID" ERR; set -E; false; echo one subshell; (false); echo two subshells; ( (false) )'запускается ERRловушка из вложенной подоболочки в случае «двух подоболочек», но не ERRловушка из промежуточной подоболочки - set -Eопция должна распространятьERRперехватывать все подоболочки, но промежуточный подоболочек оптимизирован, и поэтому его не удается запустить ERR.

жилль
источник
2
@Kusalananda Нет. ( x=out; (x=in; echo $x))
Жиль
2
@ flow2k Это порядок расширения для вещей, происходящих на одном уровне. Но вы также должны учитывать, как расширение сочетается с оценкой. Когда расширение требует оценки вложенной конструкции, внутренняя конструкция оценивается первой. Так, например, чтобы оценить echo $(x=2; echo $x), фрагмент $(x=2; echo $x)нужно расширить. Это требует оценки команды x=2; echo $x. Расширение $xпроисходит во время этой оценки, после оценки детали x=2.
Жиль
2
@ flow2k Нет порядка между расширением параметра и заменой команды. Обратите внимание, что в этом предложении используются точки с запятой для разделения шагов раскрытия, но раскрытие параметров и подстановка команд находятся в одном и том же предложении, разделенном точкой с запятой (да, это неуловимо). Порядок имеет значение, когда одна из частей имеет побочный эффект, который влияет на другую часть, например (с xнеустановленной) echo $(echo foo >somefile)${x-$(cat somefile)}или echo $(echo $x),${x=1}.
Жиль
1
@Gilles; Я смущен. Если подоболочка отличается от выполнения сценария, то почему говорят, что: Запуск сценария оболочки запускает новый процесс, подоболочку. ? Кроме того, среда подоболочки должна создаваться как дубликат среды оболочки . Следовательно, ./file будет выполняться в среде подоболочек и, следовательно, он должен наследовать параметры оболочки, которые устанавливаются путем назначения переменных.
взломает
2
@haccks Определение в ABS является приблизительным, и не очень хорошим. Примеры хороши, но первые две строки этой страницы настолько упрощены, что ошибаются. Запуск сценария из другого сценария запускает новый процесс, который не является подоболочкой. В SUS определения верны (но не всегда очень легко понять). ./fileне выполняется в подоболочке. См. Также unix.stackexchange.com/q/261638 и unix.stackexchange.com/a/157962
Жиль
15

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

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

Страница man ksh делает это немного понятнее, чем bash:

man ksh:

Команда в скобках выполняется в под-оболочке без удаления неэкспортированных переменных.

man bash:

(список)

Список выполняется в среде оболочки (см. ниже КОМАНДА ИСПОЛНИТЕЛЬНОЙ СРЕДЫ). Переменные и встроенные команды, которые влияют на среду оболочки, не остаются в силе после ее завершения.

КОМАНДНАЯ ИСПОЛНИТЕЛЬНАЯ СРЕДА

Оболочка имеет среду выполнения, которая состоит из следующих параметров: [...] параметры оболочки, которые устанавливаются путем присвоения переменной [...].
Подстановка команд, команды, сгруппированные в скобках, и асинхронные команды вызываются в среде подоболочек, которая является дубликатом среды оболочки, [...]

Mikel
источник
3
Это нужно противопоставить When a simple command other than a builtin or shell function is to be executed, it is invoked in a separate execution environment that consists of the following., который содержит элемент: · shell variables and functions marked for export, along with variables exported for the command, passed in the environment(из того же man bashраздела), который объясняет, почему echo $x-script ничего не печатает, если xне экспортируется.
Йохан E