Как я могу получить pid subshell?

13

Как я могу получить pid subshell?

Например:

$ echo $$
16808

Это не работает, потому что оригинальная оболочка расширяется $$:

$ ( echo $$ )
16808

Почему одинарные кавычки не работают? После того, как исходная оболочка удаляет одинарную кавычку, не расширяется ли подоболочка $$?

$ ( echo '$$' )
$$

Почему тоже evalне работает? Является ли в evalведении субоболочке? Почему это дает мне оригинальный PID оболочки?

$ ( eval echo '$$' )
16808

Спасибо.

Тим
источник
Я предлагаю вновь открыть, потому что вопросы, на мой взгляд, существенно отличаются («как избежать $$расширения» против «другой pid в подоболочке»).
Петер - Восстановить Монику

Ответы:

12

В дополнение к bashs $BASHPIDвы можете сделать это с помощью:

pid=$(exec sh -c 'echo "$PPID"')

Пример:

(pid=$(exec sh -c 'echo "$PPID"'); echo "$$ $pid")

Вы можете превратить это в функцию:

# usage getpid [varname]
getpid(){
    pid=$(exec sh -c 'echo "$PPID"')
    test "$1" && eval "$1=\$pid"
}

Обратите внимание, что некоторые оболочки (например, zshили ksh93) НЕ запускают подпроцесс для каждой подоболочки, созданной с помощью (...); в этом случае $pidможет оказаться так же, как $$и это правильно, потому что это PID процесса, из которого getpidбыл вызван.

mosvy
источник
1
Нет. Но, пожалуйста, не думайте, что подоболочка обязательно запускается в подпроцессе ksh93, например, это не так.
Мосви
1
Он будет работать нормально в ksh93 - он всегда будет возвращать pid процесса, из которого он был вызван. Это (...)из примера, который не может порождать отдельный процесс, как это происходит в bash.
Мосви
1
Кроме того, некоторые оболочки любят zshили yashоптимизируют fork()последнюю команду в подоболочке. Они могут даже оптимизировать ответвление для подоболочки, если это последняя команда в сценарии, чтобы вы getpidмогли даже сообщить о родительском объекте $$. Вы можете определить getpidкак: getpid(){ sh -c 'echo "$PPID"'; return; }отключить, чтобы избежать проблемы.
Стефан Шазелас
1
@HaroldFischer 1. Без execили без этой оптимизации sh -c ...процесс будет внуком, а не дочерним по отношению к процессу, в котором используется $(...)подстановка команд, и $PPIDбудет pid $(...)подоболочки. Это именно то, что происходит в примере set -E+ trap ERRbash выше.
mosvy
1
@HaroldFischer 2. test "$1"проверяет, $1является ли пустая строка или нет - быстрый и грязный способ проверить, была ли эта функция получила varnameаргумент для присвоения pid или нет; использование функции не было самой яркой идеей на первом месте.
mosvy
18
$ echo $BASHPID
37152
$ ( echo $BASHPID )
18633

Из руководства:

BASHPID

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

$

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

Связанные с:

Кусалананда
источник
Спасибо. (1) Что означает «реинициализированный»? (2) Не могли бы вы также подумать, почему те способы, которые я пробовал, не работают?
Тим
@ Тим Я считаю , что это отвечает Жиль здесь . Bash просто не обновляется $$в подоболочках.
Кусалананда
Вы имеете в виду, что я всегда должен использовать $ BASHPID вместо $$ в любом случае в bash? Когда я буду использовать какие?
Тим
@Tim Это зависит от того, хотите ли вы в подоболочке получить идентификатор процесса скрипта или подоболочки. Обе возможности предоставляются, и какая из них является правильной, зависит от приложения. Более конкретный ответ не может быть дан на это.
Кусалананда
1
@Tim PID родительской оболочки подоболочки не может быть надежно найден, если вы не решите сохранить $BASHPIDпеременную и использовать ее в подоболочке. Есть $PPID, но это родительский PID оболочки в том же смысле, что $$и PID оболочки (он не сбрасывается в подоболочке). Там нет $BASHPPIDпеременной.
Кусалананда