20

According to its documentation, bash waits until all commands in a pipeline have finished running before continuing

The shell waits for all commands in the pipeline to terminate before returning a value.

So why does the command yes | true finish immediately? Shouldn't the yes loop forever and cause the pipeline to never return?


And a subquestion: according to the POSIX spec, shell pipelines may choose to either return after the last command finishes or wait until all the commands finish. Do common shells have different behavior in this sense? Are there any shells where yes | true will loop forever?

2
  • yes | tee >(true) >/dev/null will do as you expect, btw, as tee continues until all writers are dead, so true exiting won't disrupt it entirely. Commented Nov 16, 2015 at 17:17
  • 1
    true is basically a {return 0;} program, so I wouldn't expect it to run for long, let alone forever. Commented Nov 16, 2015 at 19:48

2 Answers 2

38

When true exits, the read side of the pipe is closed, but yes continues trying to write to the write side. This condition is called a "broken pipe", and it causes the kernel to send a SIGPIPE signal to yes. Since yes does nothing special about this signal, it will be killed. If it ignored the signal, its write call would fail with error code EPIPE. Programs that do that have to be prepared to notice EPIPE and stop writing, or they will go into an infinite loop.

If you do strace yes | true1 you can see the kernel preparing for both possibilities:

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 is watching events via the debugger API, which first tells it about the system call returning with an error, and then about the signal. From yes's perspective, though, the signal happens first. (Technically, the signal is delivered after the kernel returns control to user space, but before any more machine instructions are executed, so the write "wrapper" function in the C library does not get a chance to set errno and return to the application.)


1 Sadly, strace is Linux-specific. Most modern Unixes have some command that does something similar, but it often has a different name, it probably doesn't decode syscall arguments as thoroughly, and sometimes it only works for root.

15
  • 3
    @hugomg in that case, the pipe is totally irrelevant.
    – muru
    Commented Nov 16, 2015 at 16:25
  • 4
    @hugomg because nothing in yes is connected to pipe.
    – muru
    Commented Nov 16, 2015 at 16:27
  • 4
    It's true, that is a demonstration of the documented behavior "wait until all commands finish before terminating the pipeline". It just prevents yes from getting SIGPIPE, since the FD it's writing to isn't connected to a pipe.
    – Tom Hunt
    Commented Nov 16, 2015 at 16:27
  • 2
    @hugomg, it's looping forever in the same way that yes >/dev/null is looping forever. It demonstrates nothing at all about pipelines that isn't also true of simple commands (as the waiting-for-termination behavior Tom points out is true of simple commands as well). Commented Nov 16, 2015 at 17:11
  • 2
    @zwol: I think we are using our terms with slightly different meanings here, or thinking about things from slightly different perspectives… but in either case, write() (the function in libc) does not return (transfer control to the PC following it) until after the signal handler has run, but since the signal handler terminates the program, control is never transferred and therefore write() never returns. Yes, that's implemented in the kernel by having some xxx_write() function return -EPIPE, but we're debugging a user-space program and uninterested in that. Commented Nov 17, 2015 at 3:00
5

Are there any shells where yes | true will loop forever?

Unlikely, since the yes command is using the pipe, and it will fail when the pipe is broken. sleep on the other hand, doesn't use the pipe, so:

sleep 100000000 | true

will run for 100000000 seconds, at least.

11
  • 2
    Be careful on all modern shells that don't fork for the last (rightmost) builtin command in a pipe and where true is a builtin. This applies to recent versions of the Bourne Shell, ksh93, zsh. If you hit ^Z when such a command is running, this will suspend the sleep and the shell will never be able to recover without external help.
    – schily
    Commented Nov 16, 2015 at 16:40
  • 3
    zsh 4.3.4 (i386-pc-solaris2.11) here, so it seems this was modified recently. Interesting idea, I'll need to have a look whether I can implement a similar fix for the Bourne Shell. There is still a question how it works, and which tty process group is used as in the Bourne Shell the fact that the righmost command is a builtin is discovered after the process group for the sleep was already set up for ever.
    – schily
    Commented Nov 16, 2015 at 16:45
  • 2
    @CharlesDuffy from what I understand, schily maintains a version of sh, to which he backports improvements from modern shells. He's posted about it here, somewhere.
    – muru
    Commented Nov 16, 2015 at 17:16
  • 3
    The Bourne Shell in the heirloom archives was maintained until ~ 2007 but never was made fully portable as it still contains calls to sbrk(). A portable and maintained version is in the schily tools bundle and @Charles Duffy already discovered a location for information ;-)
    – schily
    Commented Nov 16, 2015 at 18:06
  • 2
    @muru many of the features I backported to the Bourne Shell are from my bsh (Berthold Shell from VBERTOS, a virtual memory enhanced version of UNOS - the first UNIX clone). Bsh did get many csh features in 1984 and 1985 but the alias mechanism from UNOS was superior to the one from csh in 1980 already. Other new Bourne Shell features are from POSIX, to let it approach POSIX compliance.
    – schily
    Commented Nov 16, 2015 at 18:09

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .