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

5

Em um Ubuntu 12.04, com o GNU bash, versão 4.2.25 (1) -release (x86_64-pc-linux-gnu), Eu 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 é a minha expectativa: o primeiro bash é onde eu executei esses comandos; o segundo bash é o que comecei com 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 o pstree se tornou um subprocesso do original bash ? E por que a resposta da pergunta anterior não se aplica ao segundo bash -c ... ?

    
por Roun 22.06.2014 / 04:56

1 resposta

7

Eu teria dito que é simplesmente Tail Ligue Otimização , mas na verdade (como o último link aponta), bash não otimiza as chamadas finais. Parece apenas otimizar o caso em que o comando a ser executado é um comando simples (isto é, não um comando composto).

O segundo comando não é uma chamada final de pstree , porque pstree não é a última coisa na linha de comando. (É uma chamada de retorno de echo , mas echo é normalmente um built-in, portanto, nenhum subprocesso será criado, independentemente disso.)

Para salvar a leitura de todos esses links (embora eu espero que eles sejam interessantes), a idéia é que se você souber que uma função / programa / qualquer coisa retornará imediatamente depois de chamar outra função / programa / qualquer coisa e que o valor retornado será o valor retornado pela função chamada / programa / qualquer coisa, então você pode também reutilizar o quadro de pilha atual (ou processo, no caso de um script de shell) em vez de empurrar um novo quadro de pilha (criando um novo processo) , chamando a função (executando o script) e retornando. Em um script de shell, você pode fazer isso manualmente usando exec para o último comando, mas seria possível para o shell fazer isso automaticamente.

zsh e ksh 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 tenho instalado, 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)
    
por 22.06.2014 / 05:48