Трубопровод не требует, чтобы первый экземпляр заканчивался до запуска другого. На самом деле, все это на самом деле делает это перенаправляет стандартный вывод первого экземпляра в стандартный ввод из второй, так что они могут работать одновременно ( так как они должны для вилочного бомбы на работу).
Ну, что именно на выходе :? что передается другому :?
':' ничего не записывает в другой экземпляр ':', он просто перенаправляет стандартный вывод на стандартный ввод второго экземпляра. Если он что-то пишет во время своего выполнения (чего он никогда не сделает, так как он ничего не делает, кроме как разветвляется), он перейдет к стандартному входу другого экземпляра.
Это помогает представить stdin и stdout в виде кучи:
Все, что записано в stdin, будет готово, когда программа решит читать с него, в то время как stdout работает так же: куча, в которую вы можете писать, поэтому другие программы могут читать с нее, когда захотят.
Таким образом, легко представить себе ситуации, такие как канал, который не имеет связи (две пустые группы) или несинхронизированные операции записи и чтения.
Как именно это выполняется дважды? По моему мнению, ничто не передается второму :до тех пор, пока первое :не завершит его выполнение, которое фактически никогда не закончится.
Так как мы просто перенаправляем ввод и вывод экземпляров, не требуется, чтобы первый экземпляр заканчивался до запуска второго. Обычно желательно, чтобы оба запускались одновременно, чтобы второй мог работать с данными, которые анализируются первым на лету. Вот что происходит здесь, оба будут вызваны без необходимости ждать, пока первый закончит. Это относится ко всем цепочкам командных линий.
Я думаю, что та же логика применима к: () {: |: &} ;: и
:(){ : & };:
Делает ту же работу, что и
:(){ :|: & };:
Первый из них не будет работать, потому что, хотя он работает сам по себе рекурсивно, функция вызывается в фоновом режиме ( : &). Первый :не ждет, пока «потомок» :вернется, прежде чем завершится сам, так что в итоге у вас, вероятно, будет только один экземпляр :выполнения. Если бы у вас было :(){ : };:это, это сработало бы, так как первый :будет ждать :возвращения «ребенка» , который будет ждать возвращения своего «ребенка» :, и так далее.
Вот как будут выглядеть разные команды с точки зрения количества запущенных экземпляров:
:(){ : & };:
1 экземпляр (вызовы :и выходы) -> 1 экземпляр (вызовы :и выходы) -> 1 экземпляр (вызовы :и выходы) -> 1 экземпляр -> ...
:(){ :|: &};:
1 экземпляр (вызов 2 :и выход) -> 2 экземпляра (каждый вызов 2 :и выход) -> 4 экземпляра (каждый вызов 2 :и выход) -> 8 экземпляров -> ...
:(){ : };:
1 экземпляр (вызывает :и ждет его возврата) -> 2 экземпляра (дочерний вызов другого :и ждет его возврата) -> 3 экземпляра (дочерний вызов другого :и ожидает его возврата) -> 4 экземпляра -> ...
:(){ :|: };:
1 экземпляр (вызывает 2 :и ждет их возврата) -> 3 экземпляра (дети вызывают 2 :и ждут, пока они вернутся) -> 7 экземпляров (дети вызывают 2 :и ждут, пока они вернутся) -> 15 экземпляров -> ...
Как вы можете видеть, вызов функции в фоновом режиме (с использованием &) на самом деле замедляет форк-бомбу, потому что вызываемый будет выходить до того, как вызванные функции вернутся.
:|:-вторых:, не нужно ждать завершения первого.