Um subshell começa como uma cópia quase idêntica do processo shell original. 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. Mesmo a variável $$
special mantém o mesmo valor em subshells: é 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. Bash define BASHPID
para o PID do processo de shell, que muda em subshells. Bash, zsh e mksh organizam para que $RANDOM
produza valores diferentes no pai e no subnível. Mas, além de casos especiais 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 de seu criador. Alguns outros meios de criar subshells modificam alguns descritores de arquivos antes de executar o código do usuário; por exemplo, o lado esquerdo de um tubo é executado em uma 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 subshells não herdam traps customizados: sinais ignorados ( trap '' SIGNAL
) permanecem ignorados no subshell, mas outros traps ( trap CODE
SIGNAL) são redefinidos para a ação padrão 4 .
Um subshell é diferente da execução de um script. Um script é um programa separado. Este programa separado pode coincidentemente ser também um script que é executado pelo mesmo intérprete que o pai, mas esta coincidência não dá ao programa separado qualquer visibilidade especial nos dados internos do pai. As variáveis não exportadas são dados internos, portanto, quando o intérprete para o script de shell filho é executado , ele não vê esses dados. variáveis. Variáveis exportadas, ou seja, variáveis de ambiente, são transmitidas para programas executados.
Assim:
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 em
x=1
perl -le 'print $x'
ou
x=1
python -c 'print x'
1 Uma exceção é o ksh93
shell onde o bifurcado é otimizado e a maioria de seus efeitos colaterais é emulada.
2 Semanticamente, são cópias. De uma perspectiva de implementação, há muito compartilhamento acontecendo.
3 Para o lado direito, depende do shell.
4 Se você testar isso, observe que coisas como $(trap)
podem relatar as armadilhas do shell original. Note também que muitos shells possuem bugs em casos de canto envolvendo traps. 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 o ERR
trap do subshell aninhado em o caso “two subshells”, mas não o ERR
trap da subshell intermediária - set -E
opção deve propagar o ERR
trap para todas as subshells, mas o subshell intermediário é otimizado e não está lá para executar o ERR
trap.