Существует ли подоболочка, созданная при запуске команды `sh -c "command"`, новая оболочка или ничего из этого?

В bash, когда я запускаю следующую команду:

sh -c "command"

там создан подоболочка, а затем command выполняется?

Я предполагаю, что команда будет работать в текущей оболочке, но я не уверен. Это предположение исходит из того факта, что я уже тестировал с помощью следующих команд:

echo $BASHPID, $BASH_SUBSHELL

а также

sh -c "echo $BASHPID, $BASH_SUBSHELL"

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

7 ответов

Решение

Я думаю, что с примерами можно понять ситуацию
(в моем случае sh является символической ссылкой на /bin/dash).
Я сделал это тесты для sh:

echo $$ ; sh -c 'echo $$ ; sh -c '"'"'echo $$'"'"' '
16102
7569
7570

Три разные PIDтри разных shell, (Если есть разные оболочки, то не появляется подполочка).


Аналогичным образом для BASH

echo $$ $BASHPID, $BASH_SUBSHELL ; bash -c 'echo $$  $BASHPID $BASH_SUBSHELL  ; bash -c '"'"'echo  $$ $BASHPID $BASH_SUBSHELL '"'"' '
16102 16102, 0
10337 10337 0
10338 10338 0

Три разные $BASHPID без разницы $BASH_SUBSHELL (см. примечание ниже для различий между $$ а также $BASHPID).
Если бы мы были в подоболочке, которая не требует повторной инициализации, то $$ а также $BASHPID должно быть по-другому.
Таким же образом $BASH_SUBSHELL не увеличивается, это всегда 0, Итак, 2 подсказки, чтобы еще раз сказать, что не появилось ни одной новой оболочки, у нас есть только новые оболочки.


От man bash (Выпуск 4.2.45(1)) Я сообщаю о некоторых важных моментах появления подоболочки:

  1. Каждая команда в конвейере выполняется как отдельный процесс (т. Е. В подоболочке).

  2. Если команда завершается оператором управления &, оболочка выполняет команду в фоновом режиме в подоболочке. Оболочка не ожидает завершения команды, и статус возврата равен 0.

    Команды, разделенные; выполняются последовательно; оболочка ожидает завершения каждой команды по очереди. Статус возврата - это статус выхода последней выполненной команды....

  3. ( список ) список выполняется в среде подоболочки
    {список; } список просто выполняется в текущей среде оболочки.

  4. Копроцесс - это команда оболочки, которой предшествует зарезервированное слово coproc. Сопроцесс выполняется асинхронно в подоболочке...

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

Примечания:

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

  • Для различий между использованием одинарной кавычки '' двойная цитата "" Вы можете увидеть этот вопрос. Пусть помнят только то, что если вы пишете команды в двойных кавычках"" переменные будут оцениваться с помощью раскрытия параметров из исходной оболочки, если extquote включен, как это по умолчанию из shopt, (см. 4.3.2 Встроенный модуль в Справочном руководстве Bash)

    *extquote* Если установлено, кавычки $'string' и $"string" выполняются в расширениях ${parameter}, заключенных в двойные кавычки. Эта опция включена по умолчанию.

Для дальнейших ссылок вы можете найти полезные, например,

Я бы сказал, нет.

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

Примеры:

  • Трубопроводы: echo x | read y
  • Подстановка команд: line=$( read < file && echo "$REPLY" )

В большинстве случаев это приведет к тому, что процесс оболочки разветвляется сам.

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

Суть в том, что подоболочка всегда создается неявно.

Бег sh -c ... создаст новый процесс оболочки, который отбросит большую часть состояния и начнет с нуля. Это означает, что нормальные (локальные, глобальные) переменные оболочки исчезли. Естественно, новая оболочка будет иметь копию всех экспортируемых переменных.


Другая интерпретация вопроса может быть -c опция fork новый процесс для запуска ... ? Ответ: Нет. Это не так. Тем не менее, определенные команды передаются в -c Аргумент может потребовать, чтобы оболочка порождала подоболочку, как если бы она была частью скрипта или вводилась в интерактивном режиме.

Я также проверил это, и нет, я не думаю, что это (-cПризывает подоболочку. Если мы ссылаемся на sh сама да sh это оболочка, которая вызывается, но не в том случае, если речь идет о подоболочке внутри sh сам. Мы можем убедиться в этом, выполнив такую ​​команду:

# bash -c 'pstree -p; echo Value of PPID in Callee: $PPID; echo Callee PID: $BASHPID / $$'; echo "Caller PID: $$"
bash(28860)───bash(17091)───pstree(17092)
Value of PPID in Callee: 28860
Callee PID: 17091 / 17091
Caller PID: 28860

Как вы можете видеть, вызываемая оболочка (17091) и оболочка вызывающей стороны (28860) подключены напрямую как дочерний родительский элемент. Там нет ничего между ними. $BASHPID а также $$ даже одинаковы, в этом случае должно быть иначе, если вы находитесь на подоболочке. Это просто говорит о том, что при вызове команд с -c,

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

# bash -c 'pstree -p'; echo "Caller PID: $$"
bash(28860)───pstree(17124)
Caller PID: 28860

Там bash спасает себя от разветвления и решил просто напрямую выполнить exec() единственную команду. Вы можете подумать, что, возможно, bash всегда делает это с последней командой, если команда ссылается на внешний исполняемый двоичный файл, но нет, это не так:

# bash -c 'echo Value of PPID in Callee: $PPID; echo Callee PID: $BASHPID / $$; pstree -p'; echo "Caller PID: $$"
Value of PPID in Callee: 28860
Callee PID: 17128 / 17128
bash(28860)───bash(17128)───pstree(17129)
Caller PID: 28860

Теперь о

echo $BASHPID, $BASH_SUBSHELL

а также

sh -c "echo $BASHPID, $BASH_SUBSHELL"

и результаты одинаковы.

Должно быть то же самое, если echo $BASHPID, $BASH_SUBSHELL выполняется в той же оболочке, так как "echo $BASHPID, $BASH_SUBSHELL" сначала раскрывается оболочкой, которая интерпретирует команду до ее передачи в качестве аргумента sh, Если BASHPID скажем так 28860 а также BASH_SUBSHELL 0, то расширенное значение "echo $BASHPID, $BASH_SUBSHELL" является 'echo 28860, 0' в этом случае команда будет на самом деле sh -c 'echo 28860, 0', Правильный путь к этому на самом деле заключается в использовании одинарной кавычки, чтобы разрешить интерпретацию только в контексте новой вызванной оболочки: sh -c 'echo $BASHPID, $BASH_SUBSHELL', хотя я не совсем уверен, будет ли сама команда полезна для тестирования.

Так что в основном я говорю, что тест echo $BASHPID, $BASH_SUBSHELL + sh -c "echo $BASHPID, $BASH_SUBSHELL" ничего не доказывает, если -c вызывает скорлупу или нет, и тот парень, который сказал вам, что он может ввести в заблуждение, поскольку переменные могут быть заменены, является правильным.

Тем не менее, мой собственный тест показал, что Bash действительно не вызывает подоболочки (-c).

Проверь это:

$ name=foo
$ echo $name
foo
$ sh -c "echo $name"
foo
$ sh -c 'echo $name'

$ 

Тебе нужно ' вместо " в вашей команде. еще $name будет оценен в foo перед казнью

И чтобы ответить на ваш вопрос, да, он создает / порождает новую оболочку.

Sh порождает подоболочку. Но идентификатор subshell остается прежним

21934 pts/0    Ss     0:00 -bash
21963 pts/0    S      0:00  \_ /usr/bin/sudo -u root -H /bin/bash -c  export AUDITUSER=ch002854; cd $HOME && exec -a '-bash' /bin/bash
22031 pts/0    S      0:00      \_ -bash
 2969 pts/0    S      0:00          \_ sleep 1000
 2993 pts/0    S      0:00          \_ sleep 1000
 3726 pts/0    R+     0:00          \_ ps af

С sh -c он просто запустит команду. Это приведет к повторной инициализации переменных среды и, следовательно, сбрасывает $BASH_SUBSHELL в значение по умолчанию 0.

# sh -c 'echo $BASHPID, $BASH_SUBSHELL'
12671, 0

Хотя с помощью `` или () вы можете создать подоболочку

# (echo $BASHPID, $BASH_SUBSHELL)
13214, 1

Независимо от удара

Выполнение этой простой строки покажет вам многое:

$ echo $$;sh -c 'echo $$;ps axfw | grep -B2 $$'
14152
12352
14147 ?        S      0:00 xterm
14152 pts/4    Ss     0:00  \_ bash
12352 pts/4    S+     0:00      \_ sh -c echo $$;ps axfw | grep -B2 $$
12353 pts/4    R+     0:00          \_ ps axfw
12354 pts/4    S+     0:00          \_ grep -B2 12352

Это явно ребенок, но что такое недоработка?

Ну, мой текущий запущенный pid оболочки - 14152

$ echo $$ $BASHPID $BASH_SUBSHELL 
14152 14152 0
$ (echo $$ $BASHPID $BASH_SUBSHELL)
14152 12356 1
$ ( (echo $$ $BASHPID $BASH_SUBSHELL) )
14152 12357 2

Хорошо, хорошо: BASHPID - это динамическая переменная, которая всегда принимает значение выполнения pid:

$ echo $BASHPID | sed \$a$BASHPID | sed \$a$BASHPID
12371
12372
12373

Хммм, где мой текущий рабочий пид?

$ sed "\$a$BASHPID $$" < <(echo "$BASHPID $$"| sed "\$a$BASHPID $$")
12386 14152
12387 14152
14152 14152

Ну, я их нашел!

$ echo $BASHPID $$;(echo $BASHPID $$;ps axfw | grep -B3 $BASHPID)
14152 14152
12469 14152
14152 pts/4    Ss     0:00  \_ bash
12469 pts/4    S+     0:00      \_ bash
12470 pts/4    R+     0:00          \_ ps axfw
12471 pts/4    S+     0:00          \_ grep -B3 12471

Заключение

Когда ты

$ echo $BASHPID $$ $BASH_SUBSHELL;(echo $BASHPID $$ $BASH_SUBSHELL)
14152 14152 0
12509 14152 1

используйте скобку или пробел (|), вы создадите подоболочку, которая является раздвоенным потомком работающей оболочки.

Но когда ты

$ echo $BASHPID $$ $BASH_SUBSHELL;bash -c 'echo $BASHPID $$ $BASH_SUBSHELL'
14152 14152 0
12513 12513 0

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

Примечание: если ребенок не связан с родителем, они используют одинаковые дескрипторы файлов ввода-вывода. Так что, если вы закроете окно (pts/4 в моем прогоне), вы отправите SIGHUP всем процессам, использующим их.

В этом ответе я думаю о подоболочке точно так же, как и о подпроцессе.

Прежде всего, не смущайтесь расширением переменных. Если вы напишите переменную в двойных кавычках, она будет раскрыта вызывающей оболочкой, а не командой sh или ее -c. Так

sh -c "echo $$" 

дает PID вызывающей оболочки, потому что она расширяет $$ до вызова sh, тогда как

sh -c 'echo $$' 

выдает PID команды sh, которая была вызвана, потому что одинарные кавычки сообщают вызывающей оболочке передать строку $$ в sh без изменений.

Тем не менее, общий ответ на ваш вопрос:

  1. Команда sh, безусловно, является подоболочкой, вызываемой родительской оболочкой, когда она видит 'sh'
  2. Когда вы заметите, что аргумент -c рассматривается им как сценарий оболочки, вы увидите, что вы получите грандиозную оболочку вызванного sh всякий раз, когда его получит обычный сценарий оболочки.

Больше на предмете 2: Некоторые встроенные оболочки создают подоболочки; другие нет. Страница man sh говорит об этом; проверьте встроенное "if" или конструкции, которые используют обратные кавычки ``. Использование труб также вызывает появление подоболочек. Вы также можете сознательно форсировать подоболочку, заключая команды в скобки. Однако включение команд в фигурные скобки {} само по себе НЕ вызывает подоболочку.

sh предназначен для запуска команд, так что в большинстве случаев у вас будут подоболочки. Заметными исключениями являются встроенный регистр, назначение переменных, настройка параметров и. команда.

Другие вопросы по тегам