При trueвыходе сторона чтения канала закрывается, но yesпродолжает пытаться выполнить запись в сторону записи. Это условие называется «сломанный канал», и оно заставляет ядро отправлять SIGPIPEсигнал yes. Поскольку yesничего особенного в этом сигнале не делается, он будет убит. Если он проигнорирует сигнал, его writeвызов завершится ошибкой с кодом ошибки EPIPE. Программы, которые делают это, должны быть готовы заметить EPIPEи прекратить писать, иначе они пойдут в бесконечный цикл.
Если вы сделаете strace yes | true1, вы увидите, что ядро готовится к обеим возможностям:
write(1, "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\n"..., 4096) = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=17556, si_uid=1000} ---
+++ killed by SIGPIPE +++
straceнаблюдает за событиями через API отладчика, который сначала сообщает ему о возвращении системного вызова с ошибкой, а затем о сигнале. С yesточки зрения, однако, сигнал происходит первым. (Технически, сигнал доставляется после того, как ядро вернуло управление в пространство пользователя, но до того, как будут выполнены какие-либо машинные инструкции, поэтому writeфункция «обертка» в библиотеке C не получает возможности установить errnoи вернуться в приложение.)
1 К сожалению, straceэто специфично для Linux. В большинстве современных Unix-ов есть какая-то команда, которая делает что-то похожее, но часто имеет другое имя, вероятно, она не декодирует аргументы системного вызова так же тщательно, а иногда она работает только для root.
yes | tee >(true) >/dev/nullбудет делать, как вы ожидаете, между прочим, такteeпродолжается до тех пор, пока все писатели не умрут, поэтомуtrueвыход не нарушит его полностью.