A tubulação não exige que a primeira instância termine antes da outra iniciar. Na verdade, tudo o que está realmente fazendo é redirecionar o stdout da primeira instância para o stdin da segunda, para que eles possam estar executando simultaneamente (como é necessário para o fork bomb funcionar).
Bem, qual é exatamente a saída :
? o que está sendo passado para o outro :
?
':' não está escrevendo nada para a outra instância ':', apenas redirecionando o stdout para o stdin da segunda instância. Se ele escreve alguma coisa durante a sua execução (que nunca, uma vez que não faz nada, mas se bifurcar em si) que iria para o stdin da outra instância.
Ajuda a imaginar stdin e stdout como uma pilha:
Tudo o que está escrito no stdin será empilhado pronto para quando o programa decidir ler a partir dele, enquanto o stdout funciona da mesma maneira: uma pilha na qual você pode escrever, para que outros programas possam ler quando quiserem.
Dessa forma, é fácil imaginar situações como um canal sem comunicação (duas pilhas vazias) ou gravações e leituras não sincronizadas.
Como exatamente isso é executado duas vezes? Na minha opinião, nada é passado para o segundo :
até que o primeiro :
termine sua execução, o que realmente nunca terminará.
Como estamos apenas redirecionando a entrada e a saída das instâncias, não é necessário que a primeira instância termine antes do início da segunda. Na verdade, geralmente é desejável que ambos sejam executados simultaneamente para que o segundo possa trabalhar com os dados analisados pelo primeiro em tempo real. É o que acontece aqui, ambos serão chamados sem precisar esperar o primeiro final. Isso se aplica a todas as linhas de comandos de cadeias de tubulação .
Eu estou pensando que a mesma lógica se aplica a: () {: |: &} ;: e
:(){ : & };:
Faz o mesmo trabalho que
:(){ :|: & };:
O primeiro não funcionaria, porque, embora esteja executando recursivamente, a função está sendo chamada em segundo plano ( : &
). O primeiro :
não espera até que o "filho" :
retorne antes de terminar, então no final você provavelmente terá apenas uma instância de :
execução. Se você tivesse :(){ : };:
, funcionaria, pois o primeiro :
aguardaria o :
retorno do "filho" , o que esperaria o retorno do seu "filho" :
, e assim por diante.
Veja como seriam os comandos diferentes em termos de quantas instâncias seriam executadas:
:(){ : & };:
1 instância (chamadas :
e saídas) -> 1 instância (chamadas :
e saídas) -> 1 instância (chamadas :
e saídas) -> 1 instância -> ...
:(){ :|: &};:
1 instância (chama 2 :
's e encerra) -> 2 instâncias (cada uma chama 2 :
' e encerra) -> 4 instâncias (cada uma chama 2 :
'e encerra) -> 8 instâncias -> ...
:(){ : };:
1 instância (chama :
e aguarda o retorno) -> 2 instâncias (o filho chama outro :
e espera o retorno) -> 3 instâncias (o filho chama outro :
e espera o retorno) -> 4 instâncias -> ...
:(){ :|: };:
1 instância (chama 2 :
e espera que eles retornem) -> 3 instâncias (filhos chama 2 :
e cada um e espera que eles retornem) -> 7 instâncias (filhos chama 2 :
e cada e espera que eles retornem) -> 15 instâncias -> ...
Como você pode ver, chamar a função em segundo plano (usando &
) na verdade reduz a velocidade da bifurcação, porque o chamado termina antes que as funções chamadas retornem.
:|:
segundo,:
não é necessário esperar o primeiro ser concluído.