Por que `bash -c somecommand` às vezes não sai de um processo bash?

Por que `bash -c somecommand` às vezes não sai de um processo bash?

Em um Ubuntu 12.04, com GNU bash, versão 4.2.25(1)-release (x86_64-pc-linux-gnu), tentei o seguinte comando:

$ bash -c 'pstree -s $$'
init───sshd───sshd───sshd───bash───pstree
$ bash -c 'pstree -s $$;echo'
init───sshd───sshd───sshd───bash───bash───pstree

Acho que o segundo está de acordo com a minha expectativa: o primeiro bashé onde executei esses comandos; a segunda bashé com a qual comecei bash -c ...; e então o segundo bash iniciará um subprocesso chamado pstree.

No entanto, estou me perguntando o que aconteceu com o primeiro. Por que o segundo bashdesapareceu e pstreese tornou um subprocesso do original bash? E por que a resposta da pergunta anterior não se aplica à segunda bash -c ...?

Responder1

Eu teria dito que é simplesmenteCauda Chamar Otimização, mas na verdade (como aponta o último link), bashnão otimiza chamadas finais. Parece otimizar apenas o caso em que o comando a ser executado é um comando simples (ou seja, não um comando composto), embora não quando há redirecionamentos. Também parece otimizar quando o comando é o último em uma lista and-or (como em true && true && cmdor false && true || cmd), mas não quando há traps no lugar (como em trap uname EXIT && cmd), pois seria incorreto fazê-lo.

O segundo comando não é uma chamada final pstree, porque pstreenão é a última coisa na linha de comando. (É uma chamada final de echo, mas echogeralmente é incorporada, portanto, nenhum subprocesso será criado para ela de qualquer maneira.)

Para economizar a leitura de todos esses links (embora eu espere que sejam interessantes), a ideia é que se você souber que uma função/programa/qualquer coisa retornará imediatamente após chamar alguma outra função/programa/qualquer coisa, e que o valor retornado será o valor retornado pela função/programa/qualquer coisa chamada, então você pode simplesmente reutilizar o quadro de pilha atual (ou processo, no caso de um script de shell) em vez de enviar um novo quadro de pilha (criando um novo processo), chamando o função (executando o script) e depois retornando. Em um script de shell, você pode fazer isso manualmente usando execo último comando, mas seria possível que o shell fizesse isso automaticamente.

zshe kshambos parecem ser capazes de fazer isso, mas não bash:

$ zsh -c 'if [[ -n foo ]]; then pstree -s $$; else echo hello; fi'
init───lightdm───lightdm───init───konsole───bash───pstree
$ ksh -c 'if [[ -n foo ]]; then pstree -s $$; else echo hello; fi'
init───lightdm───lightdm───init───konsole───bash───pstree
$ bash -c 'if [[ -n foo ]]; then pstree -s $$; else echo hello; fi'
init───lightdm───lightdm───init───konsole───bash───bash───pstree

Mas essa observação é apenas uma observação, baseada nas versões desses shells que eu instalei, então YMMV:

$ zsh --version
zsh 5.0.2 (x86_64-pc-linux-gnu)
$ ksh --version
  version         sh (AT&T Research) 93u+ 2012-08-01
$ bash --version
GNU bash, version 4.2.45(1)-release (x86_64-pc-linux-gnu)

informação relacionada