Hi Anton, > > I'm assuming you know about the SIGPIPE, or EPIPE return from > > write(2), that occurs when sed closes its stdin, nroff's stdout, and > > how that ripples back through the pipeline, causing yes(1) to stop? > > No, I don't. This really was my question. From your answer I see it's > not trivial.
Oh, it's pretty simple in the simple case. :-) (printf '.pl 1i\n.ll 2i\n'; yes 'a \n%') | nroff | sed 13q We've two pipes, nroff is reading from one and writing to another. A pipe has two ends. If the writing end of the pipe gets closed then the reader will see that as end of file, i.e. read(2) returns 0. That's what fmt sees: $ seq 10 | strace -e read fmt ... read(0, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n", 4096) = 21 read(0, "", 4096) = 0 1 2 3 4 5 6 7 8 9 10 $ But what if the opposite happens? What if the reading end of the pipe is closed by the reader? There's no `end of file' expected by the writer as it's not what occurs with a normal write(2) to a file. Instead, the kernel sends a SIGPIPE signal to the process that attempts to write(2) to a pipe where the reader has closed it. I can show that by having a Bourne shell attempt to write(2) four bytes, "foo\n", to its stdout, down a pipe that has already had file descriptor zero, stdin, closed. The trap causes a diagnostic on stderr; that's still open. $ sh -c '(trap "echo sigpipe >&2" PIPE; echo foo)' | <&- sigpipe $ signal(7) documents the default behaviour on receiving SIGPIPE is to terminate; the process ends. That's often ideal because it stops the writer beavering away when the reader has no interest, or ability now it's closed stdin, to read the writer's output. No special programming logic is needed at every point a program may write(2). Many pipelines make use of this efficiency, and it allows for `endless' sources of input to be used. As well as yes(1), there's tr -dcs 'a-z \n' ' \n' </dev/urandom for example. As long as a later bit of the pipeline closes its stdin, and that also happens when it exits, then SIGPIPEs will ripple back down the pipeline, terminating each process. A process can choose to ignore SIGPIPE; it will then see write(1) return -1, error, with errno set to EPIPE. That was the other alternative I mentioned. Note, Python is brain damaged in this regard, so I start my command-line Python scripts that need to be good Unix subjects with signal.signal(signal.SIGPIPE, signal.SIG_DFL) # Sanity restored. Cheers, Ralph.