Создание оболочки Bash с помощью фигурных скобок

31

В соответствии с этим , размещение списка команд между фигурными скобками приводит к тому, что список выполняется в текущем контексте оболочки. Никакая подоболочка не создана .

Используя, psчтобы увидеть это в действии

Это иерархия процессов для конвейера процессов, выполняемого непосредственно в командной строке. 4398 - это PID для оболочки входа в систему:

sleep 2 | ps -H;
  PID TTY          TIME CMD
   4398 pts/23   00:00:00 bash
   29696 pts/23   00:00:00   sleep
   29697 pts/23   00:00:00   ps

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

{ sleep 2 | ps -H; }
   PID TTY          TIME CMD
    4398 pts/23   00:00:00 bash
    29588 pts/23   00:00:00   sleep
    29589 pts/23   00:00:00   ps

Теперь это иерархия процессов, когда сам sleepконвейер помещается в фигурные скобки (так что всего два уровня скобок)

{ { sleep 2; } | ps -H; }
  PID TTY          TIME CMD
   4398 pts/23   00:00:00 bash
   29869 pts/23   00:00:00   bash
   29871 pts/23   00:00:00     sleep
   29870 pts/23   00:00:00   ps

Почему bashнужно создавать подоболочку для запуска sleepв третьем случае, когда в документации говорится, что команды между фигурными скобками выполняются в текущем контексте оболочки?

Iruvar
источник
Интересно, я думаю, это потому, что в третьем случае внутренняя группа является частью конвейера, и, следовательно, она выполняется в под-оболочке, например, как любой другой вызов функции, который является частью конвейера. Имеет ли это смысл?
Мирослав Кошкар
2
Я бы не сказал «Оболочка должна» только потому, что это так… Конвейеры не выполняются в контексте оболочки. Если конвейер состоит только из внешних команд, тогда достаточно создания подпроцессов. { sleep 2 | command ps -H; }
Хауке Лагинг

Ответы:

26

В конвейере все команды выполняются одновременно (со своим stdout / stdin, соединенным каналами), поэтому в разных процессах.

В

cmd1 | cmd2 | cmd3

Все три команды выполняются в разных процессах, поэтому как минимум две из них должны выполняться в дочернем процессе. Некоторые оболочки запускают один из них в текущем процессе оболочки (если он встроен как, readили если конвейер является последней командой скрипта), но bashзапускает их все в своем отдельном процессе (кроме как с lastpipeопцией в последних bashверсиях и при некоторых конкретных условиях). ).

{...}групповые команды. Если эта группа является частью конвейера, она должна выполняться в отдельном процессе, как простая команда.

В:

{ a; b "$?"; } | c

Нам нужна оболочка, чтобы оценить, что a; b "$?"это отдельный процесс, поэтому нам нужна подоболочка. Оболочка может оптимизироваться, не разветвляясь, bпоскольку это последняя команда, которая будет запущена в этой группе. Некоторые оболочки делают это, но, видимо, нет bash.

Стефан Шазелас
источник
« Все три команды выполняются в разных процессах, поэтому по крайней мере две из них должны выполняться в подоболочке. » Зачем нужна подоболочка в этом случае? Может ли родительская оболочка не порождать процессы дерева? Или, когда вы говорите « должен работать в подоболочке », вы имеете в виду, что оболочка сама разветвится, а затем exec для каждого cmd1 cmd2 и cmd3? Если я выполню это, bash -c "sleep 112345 | cat | cat "я вижу только один созданный bash и затем 3 дочерних для него без каких-либо других чередующихся подбаш.
Хакан Баба
« Нам нужна оболочка для оценки того, что a; b« $? »- это отдельный процесс, поэтому нам нужна подоболочка». Не могли бы вы также расширить рассуждения? Зачем нам нужна подпорка, чтобы понять это? Что нужно, чтобы понять это? Я предполагаю, что разбор необходим, но что еще? , Может ли родительская оболочка не разбирать a; b "$?"? Действительно ли существует фундаментальная потребность в subheel, или, может быть, это проектное решение / реализация на bash?
Хакан Баба
@HakanBaba, я изменил это на «дочерний процесс», чтобы избежать путаницы.
Стефан
1
@HakanBaba, синтаксический анализ выполняется в родительском (процесс, который читает код, тот, который выполнял интерпретатор, если этот код не был передан eval), но оценка (выполнить первую команду, дождаться ее, запустить вторую) сделано в ребенке, тот, который имеет стандартный вывод, подключенный к трубе.
Стефан
в { sleep 2 | ps -H; }родительском bash видит, sleep 2что требуется форк / exec. Но в { { sleep 2; } | ps -H; }родительском bash, { sleep 2; }другими словами, есть некоторый код bash. Похоже, что родитель может обрабатывать fork / exec, sleep 2но рекурсивно порождает новый bash для обработки обнаруженного кода bash. Это мое понимание, имеет ли смысл?
Хакан Баба
19

Вложенные фигурные скобки, кажется, означают, что вы создаете дополнительный уровень области видимости, который требует вызова новой суб-оболочки. Вы можете увидеть этот эффект со 2-й копией Bash в вашем ps -Hвыводе.

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

пример

$ { { { sleep 20; } | sleep 20; } | ps -H; }
  PID TTY          TIME CMD
29190 pts/1    00:00:00 bash
 5012 pts/1    00:00:00   bash
 5014 pts/1    00:00:00     bash
 5016 pts/1    00:00:00       sleep
 5015 pts/1    00:00:00     sleep
 5013 pts/1    00:00:00   ps

Извлекая | ps -Hмикс только для того, чтобы мы могли видеть вложенные фигурные скобки, мы можем запустить ps auxf | lessв другой оболочке.

saml     29190  0.0  0.0 117056  3004 pts/1    Ss   13:39   0:00  \_ bash
saml      5191  0.0  0.0 117056  2336 pts/1    S+   14:42   0:00  |   \_ bash
saml      5193  0.0  0.0 107892   512 pts/1    S+   14:42   0:00  |   |   \_ sleep 20
saml      5192  0.0  0.0 107892   508 pts/1    S+   14:42   0:00  |   \_ sleep 20
saml      5068  0.2  0.0 116824  3416 pts/6    Ss   14:42   0:00  \_ bash
saml      5195  0.0  0.0 115020  1272 pts/6    R+   14:42   0:00      \_ ps auxf
saml      5196  0.0  0.0 110244   880 pts/6    S+   14:42   0:00      \_ less

Но подождите, это еще не все!

Если вы удалите каналы и воспользуетесь этой формой команды, мы увидим, чего вы на самом деле ожидаете:

$ { { { sleep 10; } ; { sleep 10; } ; sleep 10; } } | watch "ps -H"

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

Вот первый sleep 10:

  PID TTY          TIME CMD
29190 pts/1    00:00:00 bash
 5676 pts/1    00:00:00   bash
 5678 pts/1    00:00:00     sleep
 5677 pts/1    00:00:00   watch
 5681 pts/1    00:00:00     watch
 5682 pts/1    00:00:00       ps

Вот второй sleep 10:

  PID TTY          TIME CMD
29190 pts/1    00:00:00 bash
 5676 pts/1    00:00:00   bash
 5691 pts/1    00:00:00     sleep
 5677 pts/1    00:00:00   watch
 5694 pts/1    00:00:00     watch
 5695 pts/1    00:00:00       ps

Вот третий sleep 10:

  PID TTY          TIME CMD
29190 pts/1    00:00:00 bash
 5676 pts/1    00:00:00   bash
 5704 pts/1    00:00:00     sleep
 5677 pts/1    00:00:00   watch
 5710 pts/1    00:00:00     watch
 5711 pts/1    00:00:00       ps

Обратите внимание, что все три сна, хотя и вызванные на разных уровнях вложенности фигурных скобок, действительно влияют на PID 5676 Bash. Поэтому я считаю, что ваша проблема связана с использованием | ps -H.

Выводы

Использование | ps -H(то есть конвейер) вызывает дополнительную вложенную оболочку, поэтому не используйте этот метод при попытке опроса того, что происходит.

SLM
источник
Итак, «В рамках оригинальной оболочки Bash выполняются только процессы, предусмотренные на первом уровне фигурных скобок».
xealits
@xealits - это продолжение Q ты меня спрашиваешь?
СЛМ
@slm, это просто акцент на главном пункте ответа, как я его видел. Команды на первом уровне фигурных скобок выполняются в текущей оболочке, вложенные фигурные скобки создают новые оболочки. Скобки отличаются в создании подоболочки сразу, на первом уровне. Если я ошибся - поправьте меня. Но, как указывают другие, у первоначального вопроса есть и конвейеры. Таким образом создание отдельных процессов. И фигурные скобки должны создавать оболочку при использовании для отдельного процесса. Так что, возможно, это и есть причина рассматриваемого поведения.
xealits
теперь, после перечитывания вашего поста, я вижу, что был неправ - вложенное утверждение касается только случая с конвейерами. Таким образом, фигурные скобки никогда не создают новых оболочек, если вы не заключите с ними отдельный процесс - тогда они должны.
xealits
@xealits - это правильно.
SLM
7

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

$ { A=1; { A=2; sleep 2; } ; echo $A; }
2

$ { A=1; { A=2; sleep 2; } | sleep 1; echo $A; }
1
Мирослав Кошкар
источник
Мой А также показывает это.
SLM