Bash and Sed – Why Does `… | sed ‘s/^/stdout: /’` Print on Empty stdin?

bashio-redirectionprocess-substitutionsedzsh

I'm trying to understand what causes the difference in these 2 constructs that I thought were functionally equivalent:

$ ( echo foo >&2 ) 2> >(sed 's/^/stderr: /') | sed 's/^/stdout: /'   
stdout: stderr: foo
$ ( echo foo >&2 ) 2> >(sed 's/^/stderr: /') > >(sed 's/^/stdout: /')
stderr: foo

EDIT: If I understood user1133275 right, he suggests that >(sed 's/^/stdout: /') is not run unless the subshell ( echo foo >&2 ) outputs to stdout. However, that would mean that the following:

$ ( echo foo >&2 ) 2> >(sed 's/^/stderr: /') > >(echo baz)   
baz
stderr: foo

should not display baz.

EDIT 2: Perhaps also of interest, sed doesn't output stdout: on empty input, even when piped:

$ sed 's/^/stdout: /' < /dev/null
$ printf "" | sed 's/^/stdout: /'
$

Best Answer

Your first command,

( echo foo >&2 ) 2> >(sed 's/^/stderr: /') | sed 's/^/stdout: /'

in simplified form (using a temporary file to hold the data produced by echo):

{ echo foo 2>file >&2; sed 's/^/stderr: /' file; } | sed 's/^/stdout: /'

I.e., the first sed reads what is produced on standard error from the echo and writes to standard output and the second sed reads and modifies that.

Your second command,

( echo foo >&2 ) 2> >(sed 's/^/stderr: /') > >(sed 's/^/stdout: /')

in simplified form,

echo foo 2>file >&2; sed 's/^/stderr: /' file; sed 's/^/stdout: /' </dev/null

Here, the sed that gets the standard error output produces output while the other sed that gets the standard output output (which is nothing) does not produce any output (since it didn't get any input and since it did not insert or append any data).

Another way of formulating it:

First command:

( echo foo >&2 ) 2>file
sed 's/^/stderr: /' file | sed 's/^/stdout: /'

Second command:

( echo foo >&2 ) 2>file >otherfile
sed 's/^/stderr: /' file
sed 's/^/stdout: /' otherfile

In short, the second sed in the second command never reads anything. In particular, it does not read the output from the first sed as in the first command.


Using extremely simplified symbols, the first command is something like

utility-writing-to-stderr 2> >(something1) | something2

where something1 writes to standard output, which is read by something2.

The second command, using the same notation,

utility-writing-to-stderr 2> >(something1) >(something2)

i.e. something1 and something2 is not even connected to each other, and something2 can not in any way read what something1 is producing. Furthermore, since utility-writing-to-stderr does not produce anything on its standard output stream, something2 will have nothing to read from its standard input.