Pelo que li, colocar um comando entre parênteses deve executá-lo em um subshell, semelhante à execução de um script. Se isso for verdade, como ele vê a variável x se x não for exportado?
x=1
Executar (echo $x)
na linha de comando resulta em 1
Executar echo $x
um script não resulta em nada, como esperado
Responder1
Um subshell começa como uma cópia quase idêntica do processo shell original. Sob o capô, o shell chama ofork
chamada de sistema 1 , que cria um novo processo cujo código e memória são cópias 2 . Quando o subshell é criado, há 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 nos 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 ≥4.0 define BASHPID
o PID do processo shell, que muda em subshells. Bash, zsh e mksh organizam para $RANDOM
produzir valores diferentes no pai e no subshell. Mas, além de casos especiais integrados 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 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 corre em um subinvólucro 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 armadilhas personalizadas: sinais ignorados ( ) permanecem ignorados no subshell, mas outras armadilhas (trap '' SIGNAL
trap CODE
SINAL) são redefinidos para a ação padrão 4 .
Um subshell é, portanto, diferente de executar um script. Um script é um programa separado. Este programa separado pode coincidentemente ser também um script 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. 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 para programas executados.
Por isso:
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 A menos que o shell otimize a bifurcação, mas emule a bifurcação tanto quanto necessário para preservar o comportamento do código que está executando. O Ksh93 otimiza muito, outros shells geralmente não.
2 Semanticamente, são cópias. Do ponto de vista da implementação, há muito compartilhamento acontecendo.
3 Para o lado direito depende da casca.
4 Se você testar isso, observe quecoisas como$(trap)
pode relatar as armadilhas do shell original. Observe também que muitos shells apresentam bugs em casos extremos envolvendo armadilhas. Por exemploninjaljobserva 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 no caso de “dois subshells”, mas não o ERR
trap do subshell intermediário - set -E
a opção deve propagar o ERR
trap para todos os subshells, mas o subshell intermediário é otimizado e também é ' não está lá para executar sua ERR
armadilha.
Responder2
Obviamente, sim, como diz toda a documentação, um comando entre parênteses é executado em um subshell.
O subshell herda uma cópia de todas as variáveis do pai. A diferença é que quaisquer alterações feitas no subshell também não são feitas no pai.
A página de manual do ksh deixa isso um pouco mais claro que a do bash:
man ksh
:
Um comando entre parênteses é executado em um subshell sem remover variáveis não exportadas.
man bash
:
(
lista)
list é executado em um ambiente subshell (consulte AMBIENTE DE EXECUÇÃO DE COMANDO abaixo). As atribuições de variáveis e os comandos internos que afetam o ambiente do shell não permanecem em vigor após a conclusão do comando.
AMBIENTE DE EXECUÇÃO DE COMANDO
O shell possui um ambiente de execução, que consiste no seguinte: [...] parâmetros de shell que são definidos por atribuição de variáveis [...].
Substituição de comandos comandos agrupados entre parênteses e comandos assíncronos são invocados em um ambiente subshell que é uma duplicata do ambiente shell