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 bash
desapareceu e pstree
se 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), bash
nã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 && cmd
or false && true || cmd
), mas não quando há trap
s no lugar (como em trap uname EXIT && cmd
), pois seria incorreto fazê-lo.
O segundo comando não é uma chamada final pstree
, porque pstree
não é a última coisa na linha de comando. (É uma chamada final de echo
, mas echo
geralmente é 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 exec
o último comando, mas seria possível que o shell fizesse isso automaticamente.
zsh
e ksh
ambos 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)