Como posso obter o pid de um subshell?

13

Como posso obter o pid de um subshell?

Por exemplo:

$ echo $$
16808

Isso não funciona, porque o shell original se expande $$:

$ ( echo $$ )
16808

Por que as aspas simples não funcionam? Depois que o shell original remove as aspas simples, o subshell não se expande $$por si só?

$ ( echo '$$' )
$$

Por que também evalnão funciona? É evalexecutado pelo subshell? Por que isso me fornece o PID do shell original?

$ ( eval echo '$$' )
16808

Obrigado.

Tim
fonte
Sugiro reabrir, porque as perguntas são essencialmente diferentes na minha opinião ("como evitar a $$expansão" vs. "pid diferente no subshell").
peterh - Restabelece Monica

Respostas:

12

Além do bash's $BASHPID, você pode fazê-lo de forma portável com:

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

Exemplo:

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

Você pode transformá-lo em uma função:

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

Observe que alguns shells (por exemplo, zshou ksh93) NÃO iniciam um subprocesso para cada subshell criado com (...); nesse caso, $pidpode acabar sendo o mesmo que $$, o que é certo, porque esse foi o nome do PID do processo getpid.

mosvy
fonte
1
Não. Mas não suponha que um subshell seja necessariamente executado em um subprocesso - esse não é o caso ksh93, por exemplo.
mosvy
1
Funcionará bem no ksh93 - sempre retornará o pid do processo do qual foi chamado. É do (...)exemplo que pode não gerar um processo separado, como ocorre em bash.
mosvy
1
Além disso, alguns shells gostam zshou yashotimizam um fork()para o último comando em um subshell. Eles podem até otimizar a bifurcação para o subshell se esse for o último comando de um script para que você getpidpossa até relatar o pai $$. Você pode definir getpidcomo: getpid(){ sh -c 'echo "$PPID"'; return; }para desativar evitar o problema.
Stéphane Chazelas 27/11/2018
1
@HaroldFischer 1. sem execou sem essa otimização, o sh -c ...processo será um neto, em vez de um filho do processo em que uma $(...)substituição de comando é usada, e $PPIDserá o pid do $(...)subshell. É exatamente o que acontece no exemplo set -E+ trap ERRbash acima.
mosvy 5/01
1
@HaroldFischer 2. test "$1"testa se $1é uma string vazia ou não - uma maneira rápida e suja de testar se essa função recebeu um varnameargumento para atribuir o pid ou não; usar uma função não era a idéia mais brilhante em primeiro lugar.
mosvy 5/01
18
$ echo $BASHPID
37152
$ ( echo $BASHPID )
18633

Do manual:

BASHPID

Expande para o ID do processo atual do bash. Isso difere de $$certas circunstâncias, como subcascas que não exigem que o bash seja reinicializado.

$

Expande para o ID do processo do shell. Em um ()subshell, ele se expande para o ID do processo do shell atual, não para o subshell.

Relacionado:

Kusalananda
fonte
Obrigado. (1) O que significa "reinicializado"? (2) Você também pode considerar por que essas maneiras pelas quais tentei não funcionam?
Tim
@ Tim Eu acredito que isso é respondido por Gilles aqui . O Bash simplesmente não é atualizado $$em subcascas.
Kusalananda
Você quer dizer que eu sempre deveria usar $ BASHPID no lugar de $$ em qualquer caso no bash? Quando devo usar qual?
Tim
@ Tim Depende se você, em um subshell, deseja obter o ID do processo do script ou do subshell. Ambas as possibilidades são fornecidas e qual é a correta depende da aplicação. Nenhuma resposta mais específica pode ser dada a isso.
Kusalananda
1
@ Tim O PID de um shell pai de um subshell não pode ser encontrado com segurança, a menos que você planeje salvar $BASHPIDem uma variável e usá-lo no subshell. Existe $PPID, mas esse é o PID pai do shell no mesmo sentido que $$o PID do shell (não é redefinido em um subshell). Não há $BASHPPIDvariável.
Kusalananda