The :
in tee ... | :
is still a process holding the read-end of the pipeline set up by the shell, the other end of which tee
is writing to. It's just that :
exits immediately, which stops it from reading from the pipe. (For the simultaneous action of the pipeline to work, the shell has to spawn a new process for each part of the pipeline, even if it's just to process the no-op :
. In your example, that process would run the if
statement in the last part of the pipe, and then eventually exit after "running" the :
builtin.)
The usual behaviour is that when the reader of a pipe exits (the read-end file descriptors are closed), the writer gets the SIGPIPE signal on the next write, and that causes it to exit.
That's usually what you want since it means that if the right-hand side of a pipeline exits, the left-hand side also exits, and doesn't hang around continuing a potentially long task uselessly. Or (worse) get stuck helplessly trying to write to a blocked pipe that doesn't admit any writes because the data has nowhere to go.
For, tee
it doesn't look like there's any exception in the POSIX specification; the part that goes nearest is a mention of write errors to file operands:
If a write to any successfully opened file operand fails, writes to other successfully opened file operands and standard output shall continue, but the exit status shall be non-zero.
If SIGPIPE is ignored, the implementations I tested continue past the EPIPE
error that then gets returned from the write()
call.
The GNU coreutils version of tee
has the -p
and --output-error
options to control what it does when a write fails:
The default operation when --output-error
is not specified, is to exit immediately on error writing to a pipe, and diagnose errors writing to non pipe outputs.
Though the way it exits is via the SIGPIPE, so starting tee
with the signal ignored it makes it not exit.
And the default with -p
is the warn-nopipe
mode, which is described as "diagnose errors writing to any output not a pipe", as opposed to the other options that make it exit. Under the hood, it also ignores the SIGPIPE signal and then stops trying to write to the pipe.
So, with the GNU version at least, you can use tee -p ... | ...
to prevent it from exiting when the pipe reader exits. Alternatively, you could arrange for the right-hand side program to be something mimicking a black hole instead, e.g. cat > /dev/null
(which still reads and writes everything it gets, but the kernel eventually then ignores the data written to /dev/null
).
write()
fails (so the POSIX text you quote is not relevant), but where the process runningtee
receives a SIGPIPE whose default disposition is to terminate the process. If you do(trap '' PIPE; exec tee...)
instead oftee...
, then the write()s fail with EPIPE, – Stéphane Chazelas Oct 08 '22 at 11:46tee
seem to silently ignore the write errors in that case (toybox tee exits with a non-zero exit status though)). – Stéphane Chazelas Oct 08 '22 at 11:54tee
should ignore the signal. As compared to the others, if the signal is ignored, the ancient BSD tee on my mac also continues past the error, but "helpfully" prints an error message every time. – ilkkachu Oct 09 '22 at 18:09