Правило вызова subshell в Bash?


24

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

Тем не менее, это не так. В фрагменте кода A (ниже) вторая sleepкоманда не выполняется в отдельной оболочке (как определено pstreeв другом терминале). Тем не менее, в фрагменте кода В, вторая sleepкоманда делает работать в отдельной оболочке. Единственная разница между фрагментами заключается в том, что второй фрагмент содержит две команды в скобках.

Может кто-нибудь объяснить, пожалуйста, правило, когда создаются подоболочки?

КОД СНИППЕТА А:

sleep 5
(
sleep 5
)

КОД СНИППЕТА B:

sleep 5
(
x=1
sleep 5
)

Ответы:


20

Скобки всегда запускают подоболочку. Происходит следующее: bash обнаруживает, что sleep 5это последняя команда, выполняемая подоболочкой, поэтому она вызывает execвместо fork+ exec. Команда sleepзаменяет подоболочку в том же процессе.

Другими словами, базовый вариант:

  1. ( … )создать подоболочку. Исходный процесс вызывает forkи wait. В подпроцессе, который является подоболочкой:
    1. sleepявляется внешней командой, которая требует подпроцесса подпроцесса Звонок подоболочки forkи wait. В подпроцессе:
      1. Подпроцесс выполняет внешнюю команду → exec.
      2. В конце концов команда завершается → exit.
    2. wait завершает в подоболочке.
  2. wait завершает в первоначальном процессе.

Оптимизация это:

  1. ( … )создать подоболочку. Исходный процесс вызывает forkи wait. В подпроцессе, который является подоболочкой, пока он не вызывает exec:
    1. sleep это внешняя команда, и это последнее, что нужно сделать этому процессу.
    2. Подпроцесс выполняет внешнюю команду → exec.
    3. В конце концов команда завершается → exit.
  2. wait завершает в первоначальном процессе.

Когда вы добавляете что-то еще после вызова sleep, подоболочка должна быть сохранена, поэтому такая оптимизация не может произойти.

Когда вы добавляете что-то еще до вызова sleep, можно выполнить оптимизацию (и ksh делает это), но bash не делает этого (она очень консервативна с этой оптимизацией).


Subshell создается путем вызова, forkа дочерний процесс (для выполнения внешних команд) создается путем вызоваfork + exec . Но ваш первый параграф говорит о том, что fork + execэто также называется subshell. Что я здесь не так делаю?
взломает

1
@haccks fork+ execне вызывается для подоболочки, она вызывается для внешней команды. Без какой-либо оптимизации есть forkвызов для подоболочки и еще один для внешней команды. Я добавил подробное описание потока в мой ответ.
Жиль "ТАК - перестань быть злым"

Спасибо огромное за обновление. Теперь это объясняется лучше. Из этого я могу сделать вывод, что в случае (...)(в базовом случае) может или не может быть вызова, execзависит от того, есть ли у подоболочки какая-либо внешняя команда для выполнения, в то время как в случае выполнения какой-либо внешней команды это должно быть fork + exec.
хак

Еще один вопрос: эта оптимизация работает только для subshell или ее можно выполнить для команды, как dateв оболочке?
хак

@ хаки, я не понимаю вопроса. Эта оптимизация заключается в том, чтобы вызывать внешнюю команду как последнее, что делает процесс оболочки. Это не ограничивается подоболочками: сравните strace -f -e clone,execve,write bash -c 'date'иstrace -f -e clone,execve,write bash -c 'date; true'
Жиль "ТАК - перестань быть злым"

4

Из Расширенного руководства по программированию Bash :

«Как правило, внешняя команда в сценарии разветвляется на подпроцесс, а встроенная Bash - нет. По этой причине встроенные команды выполняются быстрее и используют меньше системных ресурсов, чем их внешние эквиваленты команд».

И немного дальше вниз:

«Список команд, заключенный в скобки, работает как подоболочка».

Примеры:

[root@talara test]# echo $BASHPID
10792
[root@talara test]# (echo $BASHPID)
4087
[root@talara test]# (echo $BASHPID)
4088
[root@talara test]# (echo $BASHPID)
4089

Пример использования кода OPs (с более коротким сном, потому что я нетерпеливый):

echo $BASHPID

sleep 2
(
    echo $BASHPID
    sleep 2
    echo $BASHPID
)

Выход:

[root@talara test]# bash sub_bash
6606
6608
6608

2
Спасибо за ответ, Тим. Я не уверен, что он полностью отвечает на мой вопрос. Поскольку «список команд, заключенный в круглые скобки, выполняется как подоболочка», я ожидаю, что второй sleepбудет выполняться в подоболочке (возможно, в процессе подоболочки, поскольку он является встроенным, а не подпроцессом подоболочки). Однако, в любом случае, я ожидал, что существует подоболочка, то есть подпроцесс Bash в родительском процессе Bash. Для фрагмента B выше, это не так.
застенчивый

Исправление: поскольку sleepоно не является встроенным, я ожидаю, что второй sleepвызов в обоих фрагментах будет выполняться в подпроцессе процесса subshell.
застенчивый

@ Bashful Я позволил себе взломать ваш код с моей $BASHPIDпеременной. К сожалению, то, как ты это делал, не дало тебе всей истории, которой я верю. Смотрите мой добавленный вывод в ответе.
Тим

4

Дополнительное примечание к ответу @Gilles.

Как сказал Жиль: The parentheses always start a subshell.

Тем не менее, числа, которые имеют такие суб-оболочки могут повторяться:

$ (echo "$BASHPID and $$"; sleep 1)
2033 and 31679
$ (echo "$BASHPID and $$"; sleep 1)
2040 and 31679
$ (echo "$BASHPID and $$"; sleep 1)
2047 and 31679

Как вы можете видеть, $$ повторяется, как и ожидалось, потому что (выполните эту команду, чтобы найти правильную man bashстроку):

$ LESS=+/'^ *BASHPID' man bash

BASHPID
Расширяется до идентификатора текущего процесса bash. Это отличается от $$ при определенных обстоятельствах, таких как подоболочки, которые не требуют повторной инициализации bash.

То есть: если оболочка не инициализирована повторно, $$ остается тем же.

Или с этим:

$ LESS=+/'^ *Special Parameters' man bash

Специальные параметры
$ Расширяется до идентификатора процесса оболочки. В подоболочке () она расширяется до идентификатора процесса текущей оболочки, а не подоболочки.

$$Это идентификатор текущей оболочки (не подоболочки).


1
Хороший трюк для открытия man-
страницы
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.