Regra para invocar o subshell no Bash?

24

Eu pareço entender mal a regra do Bash para criar uma subcamada. Eu pensei que parênteses sempre cria um subshell, que é executado como seu próprio processo.

No entanto, isso não parece ser o caso. No Snippet de código A (abaixo), o segundo comando sleep não é executado em um shell separado (conforme determinado por pstree em outro terminal). No entanto, no trecho de código B, o segundo comando sleep faz executado em um shell separado. A única diferença entre os trechos é que o segundo trecho tem dois comandos dentro dos parênteses.

Alguém poderia explicar a regra para quando subshells são criados?

CÓDIGO SNIPPET A:

sleep 5
(
sleep 5
)

CÓDIGO SNIPPET B:

sleep 5
(
x=1
sleep 5
)
    
por bashful 22.06.2012 / 16:14

3 respostas

20

Os parênteses sempre iniciam um subshell. O que está acontecendo é que o bash detecta que sleep 5 é o último comando executado por esse subshell, então chama exec em vez de fork + exec . O comando sleep substitui o subshell no mesmo processo.

Em outras palavras, o caso base é:

  1. ( … ) cria um subnível. O processo original chama fork e wait . No subprocesso, que é um subnível:
    1. sleep é um comando externo que requer um subprocesso do subprocesso. A subshell chama fork e wait . No sub-processo:
      1. O sub-processo executa o comando externo → exec .
      2. Eventualmente, o comando termina → exit .
    2. wait conclui no subnível.
  2. wait é concluído no processo original.

A otimização é:

  1. ( … ) cria um subnível. O processo original chama fork e wait . No subprocesso, que é um subshell até chamar exec :
    1. sleep é um comando externo e é a última coisa que esse processo precisa fazer.
    2. O subprocesso executa o comando externo → exec .
    3. Eventualmente, o comando termina → exit .
  2. wait é concluído no processo original.

Quando você adiciona algo após a chamada sleep , a subshell precisa ser mantida, portanto, essa otimização não pode acontecer.

Quando você adiciona algo antes da chamada para sleep , a otimização pode ser feita (e o ksh faz isso), mas o bash não faz isso (é muito conservador com essa otimização).

    
por 23.06.2012 / 02:45
4

Do Guia de programação avançada do bash :

"Em geral, um comando externo em um script bifurca um subprocesso, enquanto um Bash incorporado não. Por esse motivo, o built-in é executado mais rapidamente e usa menos recursos do sistema que seus equivalentes de comando externos."

E um pouco mais abaixo:

"Uma lista de comandos inserida entre parênteses é executada como um subnível."

Exemplos:

[root@talara test]# echo $BASHPID
10792
[root@talara test]# (echo $BASHPID)
4087
[root@talara test]# (echo $BASHPID)
4088
[root@talara test]# (echo $BASHPID)
4089

Exemplo de uso do código OPs (com paragens menores porque sou impaciente):

echo $BASHPID

sleep 2
(
    echo $BASHPID
    sleep 2
    echo $BASHPID
)

A saída:

[root@talara test]# bash sub_bash
6606
6608
6608
    
por 22.06.2012 / 17:57
4

Uma nota adicional para @Gilles answer.

Como dito por Gilles: The parentheses always start a subshell.

No entanto, os números que esse sub-shell pode repetir:

$ (echo "$BASHPID and $$"; sleep 1)
2033 and 31679
$ (echo "$BASHPID and $$"; sleep 1)
2040 and 31679
$ (echo "$BASHPID and $$"; sleep 1)
2047 and 31679

Como você pode ver, o $$ continua se repetindo, e isso é o esperado, porque (execute este comando para encontrar a linha correta man bash ):

$ LESS=+/'^ *BASHPID' man bash

BASHPID
Expands to the process ID of the current bash process. This differs from $$ under certain circumstances, such as subshells that do not require bash to be re-initialized.

Ou seja: se o shell não for reinicializado, o $$ será o mesmo.

Ou com isso:

$ LESS=+/'^ *Special Parameters' man bash

Special Parameters
$ Expands to the process ID of the shell. In a () subshell, it expands to the process ID of the current shell, not the subshell.

O $$ é o ID do shell atual (não o subnível).

    
por 21.02.2016 / 04:11