Ответ 1
Обновление и оговорка:
В этом ответе встревожено прошлое, в котором я уверенно утверждал, что это не так. Я считаю, что он имеет значение в его текущей форме, но, пожалуйста, помогите мне устранить другие неточности (или убедить меня, что его вообще нужно удалить).
У меня существенно пересмотренный и в основном потрошенный - этот ответ после того, как @kojiro указал, что мои методы тестирования были ошибочными (я изначально использовал ps
для поиска дочерних процессов, но это слишком медленно всегда их обнаруживать); новый метод тестирования описан ниже.
Я изначально утверждал, что не все bash подоболочки работают в своем собственном дочернем процессе, но это оказывается неверным.
Как утверждает @kojiro в своем ответе, некоторые оболочки - кроме bash - DO иногда избегают создания дочерних процессов для подоболочек, поэтому, вообще говоря, в мире оболочек не следует предполагать, что подоболочка подразумевает, что ребенок процесс.
Что касается случаев OP в bash (предполагается, что экземпляры command{n}
являются простыми командами):
# Case #1
command1 # NO subshell
var=$(command1) # 1 subshell (command substitution)
# Case #2
command1 | command2 # 2 subshells (1 for each pipeline segment)
var=$(command1 | command2) # 3 subshells: + 1 for command subst.
# Case #3
command1 | command2 ; var=$? # 2 subshells (due to the pipeline)
var=$(command1 | command2 ; echo $?) # 3 subshells: + 1 for command subst.;
# note that the extra command doesn't add
# one
Похоже, что с помощью подстановки команд ($(...)
) всегда добавляет дополнительную подоболочку в bash, как и приложение любой команды в (...)
.
Я верю, но не уверен, что эти результаты верны; здесь, как я тестировал (bash 3.2.51 на OS X 10.9.1) - скажите, пожалуйста, если этот подход ошибочен:
- Убедитесь, что запущено только 2 интерактивных оболочки bash: один для запуска команд, другой для мониторинга.
- Во 2-й оболочке я отслеживал вызовы
fork()
в 1-м с помощьюsudo dtruss -t fork -f -p {pidOfShell1}
(требуется-f
для трассировкиfork()
вызовов "транзитивно", то есть для включения тех, которые созданы самими подоболочками). -
Используется только встроенный
:
(no-op) в тестовых командах (чтобы избежать путаницы с дополнительными вызовамиfork()
для внешних исполняемых файлов); а именно:-
:
-
$(:)
-
: | :
-
$(: | :)
-
: | :; :
-
$(: | :; :)
-
-
Только подсчитанные строки вывода
dtruss
, содержащие ненулевой PID (поскольку каждый дочерний процесс также сообщает вызванный им вызовfork()
, но с PID 0). - Вычитается 1 из полученного числа, так как работа даже с встроенной оболочкой из интерактивной оболочки, по-видимому, включает по крайней мере 1
fork()
. - Наконец, предположим, что итоговый счетчик представляет количество созданных подоболочек.
Ниже я верю, что верю в свое первоначальное сообщение: когда bash создает подоболочки.
bash создает подоболочки в следующих ситуациях:
- для выражения, окруженного круглыми скобками (
(...)
)- кроме непосредственно внутри
[[ ... ]]
, где круглые скобки используются только для логической группировки.
- кроме непосредственно внутри
- для каждого сегмента трубопровода (
|
), включая первый- Обратите внимание, что каждая вовлеченная подоболочка является клоном исходной оболочки с точки зрения содержимого (по сути, подоболочки могут быть выделены из других подоболочек (до выполнения команд)).
Таким образом, модификации подоболочек в предыдущих сегментах трубопровода не влияют на более поздние. (По дизайну команды в конвейере запускаются одновременно - последовательность выполняется только через их подключенные трубы stdin/stdout.) -
bash 4.2+
имеет параметр оболочкиlastpipe
(по умолчанию отключен), что приводит к тому, что последний сегмент сегмента НЕ запускается в подоболочке.
- Обратите внимание, что каждая вовлеченная подоболочка является клоном исходной оболочки с точки зрения содержимого (по сути, подоболочки могут быть выделены из других подоболочек (до выполнения команд)).
-
для подстановки команд (
$(...)
) -
для замены процесса (
<(...)
)- обычно создает 2 подоболочки; в случае простой команды @konsolebox придумал технику, чтобы создать только 1: добавьте простую команду с помощью
exec
(<(exec ...)
).
- обычно создает 2 подоболочки; в случае простой команды @konsolebox придумал технику, чтобы создать только 1: добавьте простую команду с помощью
- фоновое исполнение (
&
)
Объединение этих конструкций приведет к более чем одной подоболочке.