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

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

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

x=1

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

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

решение1

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

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

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

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

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

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)может сообщать о ловушках оригинальной оболочки. Обратите внимание также, что многие оболочки имеют ошибки в угловых случаях, связанных с ловушками. Напримерниндзялджотмечает, что, начиная с 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

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

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

Страница руководства ksh делает это немного яснее, чем страница bash:

man ksh:

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

man bash:

(список)

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

СРЕДА ВЫПОЛНЕНИЯ КОМАНД

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

Связанный контент