Il piping non richiede che la prima istanza termini prima che inizi l'altra. In realtà, tutto ciò che sta realmente facendo è reindirizzare lo stdout della prima istanza allo stdin della seconda, in modo che possano funzionare simultaneamente (come devono fare per far funzionare la bomba a forcella).
Bene, qual è esattamente l'output di :
? cosa viene passato all'altro :
?
':' non sta scrivendo nulla sull'altra ':' istanza, sta semplicemente reindirizzando lo stdout allo stdin della seconda istanza. Se scrive qualcosa durante la sua esecuzione (cosa che non farà mai, poiché non fa altro che biforcarsi) andrebbe allo stdin dell'altra istanza.
Aiuta a immaginare lo stdin e lo stdout come una pila:
Qualunque cosa sia scritta sullo stdin verrà accumulata pronta per quando il programma decide di leggere da esso, mentre lo stdout funziona allo stesso modo: una pila in cui puoi scrivere, così altri programmi possono leggere da esso quando vogliono.
In questo modo è facile immaginare situazioni come una pipe senza comunicazione (due pile vuote) o scritture e letture non sincronizzate.
Come viene eseguito esattamente due volte? Secondo me, nulla è passato al secondo :
fino a quando il primo non :
termina la sua esecuzione, che in realtà non finirà mai.
Poiché stiamo semplicemente reindirizzando l'input e l'output delle istanze, non è necessario che la prima istanza termini prima che inizi la seconda. In realtà, in genere si desidera che entrambi vengano eseguiti contemporaneamente in modo che il secondo possa funzionare con i dati analizzati dal primo al volo. Ecco cosa succede qui, entrambi saranno chiamati senza dover aspettare che il primo finisca. Questo vale per tutte le linee di comandi di catene di tubi .
Sto pensando che la stessa logica si applica a: () {: |: &} ;: e
:(){ : & };:
Fa lo stesso lavoro di
:(){ :|: & };:
Il primo non funzionerebbe, perché anche se si sta eseguendo in modo ricorsivo, la funzione viene chiamata in background ( : &
). Il primo :
non aspetta che il "figlio" :
ritorni prima di terminare se stesso, quindi alla fine avresti probabilmente solo un'istanza di :
esecuzione. Se avessi :(){ : };:
funzionato, però, dal momento che il primo :
aspetterebbe il :
ritorno del "bambino" , che aspetterebbe il ritorno del proprio "bambino" :
, e così via.
Ecco come apparirebbero diversi comandi in termini di quante istanze sarebbero in esecuzione:
:(){ : & };:
1 istanza (chiamate :
e uscite) -> 1 istanza (chiamate :
e uscite) -> 1 istanza (chiamate :
e uscite) -> 1 istanza -> ...
:(){ :|: &};:
1 istanza (chiama 2 :
'ed esce) -> 2 istanze (ognuno chiama 2 :
' ed esce) -> 4 istanze (ognuno chiama 2 :
'ed esce) -> 8 istanze -> ...
:(){ : };:
1 istanza (chiama :
e aspetta che ritorni) -> 2 istanze (il bambino chiama un'altra :
e aspetta che ritorni) -> 3 istanze (il bambino chiama un'altra :
e aspetta che ritorni) -> 4 istanze -> ...
:(){ :|: };:
1 istanza (chiama 2 :
e attende che ritornino) -> 3 istanze (i bambini chiamano 2 :
ciascuno e attendono che ritornino) -> 7 istanze (i bambini chiamano 2 :
ciascuno e attendono che ritornino) -> 15 istanze -> ...
Come puoi vedere, chiamare la funzione in background (usando &
) in realtà rallenta la bomba a forcella, perché la chiamata uscirà prima che le funzioni chiamate ritornino.
:|:
, il secondo:
non deve attendere il primo completato.