The commands are connected with a pipe (I'm talking the system primitive here—obviously, they're connected with a
|). When the read end of the pipe (the
closed (== when
head either explicitly (
close) or implicitly (exit) closes it), attempts to write to the write end (
yes) will fail.
By default, this is not just a regular
errno failure, but a failure that results in the writing process receiving the
SIGPIPE signal. The default handler action of the SIGPIPE signal is to terminate.
In short—if you write to a broken pipe, the system will send you a
SIGPIPE and by default, a
SIGPIPE will kill you. That is why
yes terminates when
head has ended (and thereby broken the pipe).
If you ignore
SIGPIPE in the parent shell, then the commands will inherit this disposition and a
write to a broken pipe will simply cause an
errno error of
EPIPE. As it appears,
yes stringifies this error an prints it:
$ (trap "" SIGPIPE; yes | head -n 5)
yes: standard output: Broken pipe
yes: write error