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 var
variável, embora não seja exportada e, no ./file
caso, 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 strace
para 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 ./file
o 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 ./file
caso, embora os mesmos argumentos sejam baseados na chamada do sistema clone?
Respostas:
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, olheexecve
e 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.
fonte
-f
opção destrace
rastrear filhos? Isso é necessário para encontrar os executivos.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 daclone
chamada de sistema mais geral , que você observou nostrace
log. A criança executa uma parte do script de shell. O processo filho é chamado de subshell . A construção mais direta écommand1 &
:command1
executa 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 pipescommand3 | command4
(command3
executado em um subshell,command4
executado 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ávelBASHPID
como 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 aexecve
chamada 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 naexecve
chamada: 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 sobreviveexecve
.fonte