Bash: process substitution and stdin

bashprocess-substitutionstdin

The following line is obvious:

echo "bla" | foo | bar

But does the ones below do the same?

echo "bla" | bar <(foo)
echo "bla" | bar < <(foo)

Which of the foo and bar read "bla" from stdin and why?

I mean that, of course, I can just code it and check it, but I'm not sure if it's defined behavior or I'm exploiting features I should not rely on.

Best Answer

That's shell dependant and not documented AFAICS. In ksh and bash, in the first case, foo will share the same stdin as bar. They will fight for the output of echo.

So for instance in,

$ seq 10000 | paste - <(tr 1 X)'
1       X
2       X042
3       X043
4       X044
5       X045
[...]

You see evidence that paste reads every other block of text from seq's output while tr reads the other ones.

With zsh, it gets the outer stdin (unless it's a terminal and the shell is not interactive in which case it's redirected from /dev/null). ksh (where it originated), zsh and bash are the only Bourne-like shells with support for process substitution AFAIK.

In echo "bla" | bar < <(foo), note that bar's stdin will be the pipe fed by the output of foo. That's well defined behaviour. In that case, it appears that foo's stdin is the pipe fed by echo in all of ksh, zsh and bash.

If you want to have a consistent behaviour across all three shells and be future-proof since the behaviour might change as it's not documented, I'd write it:

echo bla | { bar <(foo); }

To be sure foo's stdin is also the pipe from echo (I can't see why you'd want to do that though). Or:

echo bla | bar <(foo < /dev/null)

To make sure foo does not read from the pipe from echo. Or:

{ echo bla | bar 3<&- <(foo <&3); } 3<&0

To have foo's stdin the outer stdin like in current versions of zsh.

Related Question