Um subshell começa como uma cópia quase idêntica do processo original do shell. Sob o capô, o shell chama a fork
chamada de sistema 1 , que cria um novo processo cujo código e memória são cópias 2 . Quando o subshell é criado, existem muito poucas diferenças entre ele e seu pai. Em particular, eles têm as mesmas variáveis. Até a $$
variável especial mantém o mesmo valor em subcascas: é o ID do processo do shell original. Da mesma forma, $PPID
é o PID do pai do shell original.
Alguns shells alteram algumas variáveis no subshell. O Bash define BASHPID
o PID do processo do shell, que é alterado nos subshells. Bash, zsh e mksh organizam para $RANDOM
gerar valores diferentes no pai e no subshell. Mas, além de casos especiais incorporados como esses, todas as variáveis têm o mesmo valor no subshell que no shell original, o mesmo status de exportação, o mesmo status somente leitura, etc. Todas as definições de função, definições de alias, opções de shell e outras configurações também são herdadas.
Um subshell criado por (…)
possui os mesmos descritores de arquivo que seu criador. Alguns outros meios de criação de subshells modificam alguns descritores de arquivo antes de executar o código do usuário; por exemplo, o lado esquerdo de um tubo é executado em um subshell 3 com saída padrão conectada ao tubo. O subshell também começa com o mesmo diretório atual, a mesma máscara de sinal etc. Uma das poucas exceções é que os subshells não herdam traps personalizados: os sinais ignorados ( ) permanecem ignorados no subshell, mas outros traps ( SIGNAL ) são redefinidos para a ação padrão 4 .trap '' SIGNAL
trap CODE
Um subshell é, portanto, diferente de executar um script. Um script é um programa separado. Esse programa separado pode coincidentemente ser também um script que é executado pelo mesmo intérprete que o pai, mas essa coincidência não dá ao programa separado nenhuma visibilidade especial nos dados internos do pai. Variáveis não exportadas são dados internos; portanto, quando o interpretador do script shell filho é executado , ele não vê essas variáveis. Variáveis exportadas, ou seja, variáveis de ambiente, são transmitidas aos programas executados.
Portanto:
x=1
(echo $x)
imprime 1
porque o subshell é uma replicação do shell que o gerou.
x=1
sh -c 'echo $x'
acontece de executar um shell como um processo filho de um shell, mas o x
na segunda linha não tem mais conexão com o x
na segunda linha do que no
x=1
perl -le 'print $x'
ou
x=1
python -c 'print x'
1 Uma exceção é o ksh93
shell em que o garfo é otimizado e a maioria de seus efeitos colaterais são emulados.
2 Semântica, são cópias. Do ponto de vista da implementação, há muito compartilhamento acontecendo.
3 Para o lado direito, isso depende da casca.
4 Se você testar isso, observe que coisas como$(trap)
podem relatar as armadilhas do shell original. Observe também que muitas conchas têm bugs nos casos de canto que envolvem armadilhas. Por exemplo, ninjalj observa que, a partir do bash 4.3, bash -x -c 'trap "echo ERR at \$BASH_SUBSHELL \$BASHPID" ERR; set -E; false; echo one subshell; (false); echo two subshells; ( (false) )'
executa a ERR
interceptação do subshell aninhado no caso "dois subshells", mas não a ERR
interceptação do subshell intermediário - a set -E
opção deve propagar oERR
armadilha para todas as subcascas, mas a subcamada intermediária é otimizada e, portanto, não existe para executar sua ERR
armadilha.
x=out; (x=in; echo $x)
)