При true
выходе сторона чтения канала закрывается, но yes
продолжает пытаться выполнить запись в сторону записи. Это условие называется «сломанный канал», и оно заставляет ядро отправлять SIGPIPE
сигнал yes
. Поскольку yes
ничего особенного в этом сигнале не делается, он будет убит. Если он проигнорирует сигнал, его write
вызов завершится ошибкой с кодом ошибки EPIPE
. Программы, которые делают это, должны быть готовы заметить EPIPE
и прекратить писать, иначе они пойдут в бесконечный цикл.
Если вы сделаете strace yes | true
1, вы увидите, что ядро готовится к обеим возможностям:
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
выход не нарушит его полностью.