Трубопровод не требует, чтобы первый экземпляр заканчивался до запуска другого. На самом деле, все это на самом деле делает это перенаправляет стандартный вывод первого экземпляра в стандартный ввод из второй, так что они могут работать одновременно ( так как они должны для вилочного бомбы на работу).
Ну, что именно на выходе :
? что передается другому :
?
':' ничего не записывает в другой экземпляр ':', он просто перенаправляет стандартный вывод на стандартный ввод второго экземпляра. Если он что-то пишет во время своего выполнения (чего он никогда не сделает, так как он ничего не делает, кроме как разветвляется), он перейдет к стандартному входу другого экземпляра.
Это помогает представить 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 экземпляров -> ...
Как вы можете видеть, вызов функции в фоновом режиме (с использованием &
) на самом деле замедляет форк-бомбу, потому что вызываемый будет выходить до того, как вызванные функции вернутся.
:|:
-вторых:
, не нужно ждать завершения первого.