Por que uma variável é visível em um subshell?

18

O Learning Bash Book menciona que um subshell herdará apenas variáveis ​​de ambiente e descritores de arquivos etc., e que não herdará variáveis ​​que não são exportadas:

$ var=15
$ (echo $var)
15
$ ./file # this file include the same command echo $var

$

Como eu sei, o shell criará dois subshells para ()e para ./file, mas por que no ()caso o subshell identifica a varvariável, embora não seja exportada e, no ./filecaso, não a identificou?

# Strace for () 
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25617
# Strace for ./file
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25631

Tentei usar stracepara descobrir como isso acontece e, surpreendentemente, descobri que o bash usará os mesmos argumentos para a chamada do sistema clone, então isso significa que o processo bifurcado ()e ./fileo mesmo devem ter o mesmo espaço de endereço do pai, então por que no ()caso, a variável é visível para o subshell e o mesmo não ocorre para o ./filecaso, embora os mesmos argumentos sejam baseados na chamada do sistema clone?

user3718463
fonte
vinc17 diz a verdade, mesmo que você tenha pstree quando tem sub-shell, você acredita neste assunto.
precisa saber é o seguinte

Respostas:

15

O Learning Bash Book está errado. Subshells herdam todas as variáveis. Par $$(o PID do shell original) é mantido. O motivo é que, para um subshell, o shell apenas bifurca e não executa um novo shell (pelo contrário, quando você digita ./file, um novo comando é executado, por exemplo, um novo shell; na saída strace, olhe execvee similares) . Então, basicamente, é apenas uma cópia (com algumas diferenças documentadas).

Nota: isso não é específico para o bash; isso é verdade para qualquer shell.

vinc17
fonte
Oky, mas tentei agora lançar o strace no shell e tentei executar ./file, mas não consigo encontrar nenhuma chamada para exec e, portanto, o espaço de endereço deve ser o mesmo para os dois processos, para que isso possa ser explicado?
user3718463
@ user3718463 Você também usou a -fopção de stracerastrear filhos? Isso é necessário para encontrar os executivos.
vinc17
Sim, eu descobri isso graças muito, eu estava faltando a opção -f, e por isso não consigo encontrar a chamada exec sys
user3718463
16

Você ou o livro estão confundindo um subshell com um subprocesso que é um shell.

Algumas construções de shell resultam no processo de bifurcação de um processo filho. No Linux, forké um caso especial da clonechamada de sistema mais geral , que você observou no stracelog. A criança executa uma parte do script de shell. O processo filho é chamado de subshell . A construção mais direta é command1 &: command1executa em um subshell e os comandos subsequentes são executados no shell pai. Outras construções que criam um subshell incluem substituição de comando $(command2)e pipes command3 | command4( command3executado em um subshell, command4executado em um subshell na maioria dos shells, mas não no ksh ou zsh).

Um subshell é uma cópia do processo pai, portanto, ele possui não apenas as mesmas variáveis ​​de ambiente, mas também todas as mesmas definições internas: variáveis ​​(incluindo $$o ID do processo original do shell), funções, aliases, opções etc. Antes de executar o código no subshell, o bash define a variável BASHPIDcomo o ID do processo filho.

Quando você executa ./file, isso executa um comando externo. Primeiro, o shell bifurca um processo filho; esse processo filho executa (com a execvechamada do sistema) o arquivo executável ./file. Um processo filho herda atributos de processo de seus pais: ambiente, diretório atual, etc. Aspectos internos do aplicativo são perdidos na execvechamada: variáveis, funções não exportadas, etc. são noções básicas que o kernel não conhece e eles são perdidos quando o bash executa outro programa. Mesmo que esse outro programa seja um script bash, ele é executado por uma nova instância do bash que não sabe nem se importa que seu processo pai também seja uma instância do bash. Portanto, uma variável shell (variável não exportada) não sobrevive execve.

Gilles 'SO- parar de ser mau'
fonte
Esta resposta esclareceu algumas coisas para mim. A única coisa que não entendo é esta frase no segundo parágrafo: "A criança executa uma parte do script de shell". A qual script de shell está sendo referido?
flow2k
@ flow2k O script (ou seja, o programa) que o shell está interpretando.
Gilles 'SO- stop be evil'