Since, according to zshall(1), $ZDOTDIR/.zshenv gets sourced whenever a new instance of zsh starts
Se você se concentrar na palavra "começa" aqui, você terá um tempo melhor das coisas. O efeito de fork()
é criar outro processo que comece exatamente onde o processo atual já é . Está clonando um processo existente, com a única diferença sendo o valor de retorno de fork
. A documentação está usando "starts" para significar entrar no programa desde o começo.
Seu exemplo # 3 é executado em $SHELL -c 'date; printenv; echo $$'
, iniciando um processo totalmente novo desde o início. Ele passará pelo comportamento de inicialização comum. Você pode ilustrar isso, por exemplo, trocando em outro shell: execute bash -c ' ... '
em vez de zsh -c ' ... '
. Não há nada de especial sobre o uso de $SHELL
aqui.
Os exemplos # 1 e # 2 executam subpastas. O shell fork
s em si e executa seus comandos dentro desse processo filho e, em seguida, executa sua própria execução quando a criança está pronta.
A resposta à sua pergunta # 1 é a seguinte: o exemplo 3 executa um shell inteiramente novo desde o início, enquanto os outros dois executam sub-unidades. O comportamento de inicialização inclui o carregamento de .zshenv
.
A razão pela qual eles chamam esse comportamento especificamente, que é provavelmente o que leva à sua confusão, é que esse arquivo (ao contrário de outros) carrega em shells interativos e não interativos.
Para sua pergunta # 2:
if the shells that get created in #1 and #2 are called "subshells", what are those like the one generated by #3 called?
Se você quiser um nome, pode chamá-lo de "shell filho", mas na verdade não é nada. Não é diferente de qualquer outro processo iniciado a partir do shell, seja o mesmo shell, um shell diferente ou cat
.
Para sua pergunta # 3:
is it possible to rationalize (and maybe generalize) the empirical/anecdotal findings described above in terms of the "theory" (for lack of a better word) of Unix processes?
fork
faz um novo processo, com um novo PID, que começa a ser executado em paralelo de exatamente onde este parou. exec
substitui o código atualmente em execução por um novo programa carregado de algum lugar, executado a partir do começando. Quando você gera um novo programa, você primeiro fork
e exec
desse programa no filho. Essa é a teoria fundamental dos processos que se aplica em todos os lugares, dentro e fora das conchas.
Subshells são fork
s, e todos os comandos não incorporados que você executa levam a um fork
e um exec
.
Note que $$
se expande para o PID do shell pai em qualquer shell compatível com POSIX , então você pode não estar obtendo a saída esperada, independentemente. Note também que zsh agressivamente otimiza a execução de subshell de qualquer maneira, e geralmente exec
s o último comando, ou não gera o subshell se todos os comandos estiverem seguros sem ele.
Um comando útil para testar suas intuições é:
strace -e trace=process -f $SHELL -c ' ... '
Isso imprimirá em erro padrão todos os eventos relacionados ao processo (e nenhum outro) para o comando ...
executado em um novo shell. Você pode ver o que é executado e não executado em um novo processo e onde exec
s ocorre.
Outro comando possivelmente útil é o pstree -h
, que imprimirá e realçará a árvore dos processos pai do processo atual. Você pode ver quantas camadas você está na saída.