The difference between
{ echo err >&2; echo out >&1; } | :
... and
{ echo out >&1; echo err >&2; } | :
... is the order in which the two echo
calls are made. The order matters only in terms of how quickly the first echo
can execute.
The two sides of the pipeline, { ...; }
and :
, are started concurrently, and :
does not read from its standard input stream. This means the pipe is torn down as soon as :
terminates, which will be almost immediately.
If the pipe is torn down, there is no pipe buffer for echo out >&1
to write to. When echo
tries to write to the non-existent pipe, the left-hand side of the pipeline dies from receiving a PIPE signal.
If it dies before echo err >&2
has executed, there will be no output.
So:
You will always get err
as output from { echo err >&2; echo out >&1; } | :
since echo err >&2
will not care about the state of the pipe.
You will sometimes get err
as output from { echo out >&1; echo err >&2; } | :
depending on whether :
has terminated before echo out >&1
has the chance to run. If :
terminates first, there will be no output due to the left-hand side getting killed prematurely.
I'm guessing that most people will not experience the non-deterministic behaviour of the second pipeline on most systems. Still, you have found combinations of operating systems and shell versions that show this. I have also replicated the non-deterministic behaviour under zsh
on FreeBSD, even in a single shell session (although the shell seems to tend to one behaviour rather than the other, so I needed 10+ tries to finally see the switch).
debian:11.5
container and both commands gave output. – muru Nov 11 '22 at 04:24>&1
doesn't do anything. It's redirecting stdout to itself, which is a no-op. – Barmar Nov 11 '22 at 16:53