Rurociągi nie wymagają, aby pierwsza instancja zakończyła się przed uruchomieniem drugiej. W rzeczywistości wszystko, co naprawdę robi, to przekierowanie stdout pierwszej instancji do stdin drugiej instancji , aby mogły one działać jednocześnie (tak jak muszą, aby bomba widełkowa zadziałała).
Co dokładnie jest wynikiem :
? co jest przekazywane drugiemu :
?
„:” nic nie pisze do drugiej instancji „:”, po prostu przekierowuje stdout do stdin drugiej instancji. Jeśli napisze coś podczas wykonywania (czego nigdy nie zrobi, ponieważ nie robi nic poza rozwidleniem się), przejdzie do normalnego stanu drugiej instancji.
Pomaga wyobrazić sobie stdin i stdout jako stos:
Wszystko, co zostanie zapisane na stdin, zostanie zgromadzone gotowe, gdy program zdecyduje się z niego odczytać, podczas gdy stdout działa w ten sam sposób: stos, na którym można pisać, aby inne programy mogły z niego czytać, kiedy chcą.
W ten sposób łatwo wyobrazić sobie sytuacje takie jak potok, w którym nie ma komunikacji (dwa puste stosy) lub niezsynchronizowane zapisy i odczyty.
Jak dokładnie jest to wykonywane dwukrotnie? Moim zdaniem nic nie jest przekazywane drugiemu, :
dopóki pierwszy nie :
zakończy swojego wykonania, co w rzeczywistości nigdy się nie skończy.
Ponieważ po prostu przekierowujemy dane wejściowe i wyjściowe instancji, nie ma wymogu, aby pierwsza instancja zakończyła się przed uruchomieniem drugiej. Zazwyczaj pożądane jest, aby oba działały jednocześnie, aby drugi mógł pracować z danymi analizowanymi przez pierwszy w locie. Tak się dzieje tutaj, oba zostaną wywołane bez konieczności oczekiwania na zakończenie pierwszego. Dotyczy to wszystkich linii poleceń łańcuchów rur .
Myślę, że ta sama logika dotyczy: () {: |: &} ;: i
:(){ : & };:
Wykonuje tę samą pracę, co
:(){ :|: & };:
Pierwszy nie zadziałałby, ponieważ chociaż działa on rekurencyjnie, funkcja jest wywoływana w tle ( : &
). Pierwszy :
nie czeka, aż „dziecko” :
powróci, zanim się skończy, więc na koniec prawdopodobnie miałbyś tylko jedną instancję :
działania. Gdybyś miał :(){ : };:
, zadziałałoby, ponieważ pierwszy :
czekałby na :
powrót „dziecka” , co czekałoby na :
powrót własnego „dziecka” i tak dalej.
Oto, jak wyglądałyby różne polecenia pod względem liczby uruchomionych instancji:
:(){ : & };:
1 instancja (połączenia :
i rezygnacje) -> 1 instancja (połączenia :
i rezygnacje) -> 1 instancja (połączenia :
i rezygnacje) -> 1 instancja -> ...
:(){ :|: &};:
1 wystąpienie (wywołuje 2 :
i kończy) -> 2 wystąpienia (każdy wywołuje 2 :
i kończy) -> 4 wystąpienia (każdy wywołuje 2 :
i kończy) -> 8 wystąpień -> ...
:(){ : };:
1 instancja (dzwoni :
i czeka na zwrócenie) -> 2 instancje (dziecko dzwoni do innej :
i czeka na zwrot) -> 3 instancje (dziecko dzwoni do innej :
i czeka na powrót) -> 4 instancje -> ...
:(){ :|: };:
1 instancja (wywołuje 2 :
i czeka, aż powrócą) -> 3 instancje (dzieci dzwonią po 2 :
i czekają, aż powrócą) -> 7 instancji (dzieci dzwonią po 2 :
i czekają, aż powrócą) -> 15 instancji -> ...
Jak widać, wywołanie funkcji w tle (użycie &
) powoduje spowolnienie bomby widelcowej, ponieważ odbiorca zostanie zamknięty, zanim wywołane funkcje powrócą.
:|:
drugim:
nie trzeba czekać na ukończenie pierwszego.