10

While experimenting with output redirection and process substitution I stumbled upon the following command and its resulting output:

    me@elem:~$ echo foo > >(cat); echo bar
    bar
    me@elem:~$ foo

(Yes, that empty newline at the end is intentional.)

So bash echo's bar, prints my usual prompt, echo's foo, echo's a newline, and leaves my cursor there. If I hit enter again, it'll print my prompt on a new line and leave the cursor following it (as expected when someone hits enter on an empty command line).

I was expecting it to write foo to a file descriptor, cat reads it and echo's foo, the second echo echo's bar, and then back to the command prompt. But that's clearly not the case.

Could someone please explain what's going on?

2
  • I would like to suggest looking the answers on this: unix.stackexchange.com/questions/182800/… It explains the double redirection, and would explain why you are ending with different results. Both Muru, and Michael Hormers.
    – No Time
    Commented Mar 18, 2015 at 0:21
  • For a question explicitly dealing with why the output of the process substitution appears after the prompt (and in some circumstances does not appear at all) in some versions of the Bourne Again shell, see unix.stackexchange.com/questions/471987 .
    – JdeBP
    Commented Sep 28, 2018 at 8:21

2 Answers 2

10

You may see foo displayed before bar, after bar, or even after the prompt, depending on timing. Add a little delay to get consistent timing:

$ echo foo > >(sleep 1; cat); echo bar; sleep 2 
bar
foo
$

bar appears immediately, then foo after one second, then the next prompt after another second.

What's happening is that bash executes process substitutions in the background.

  1. The main bash process starts a subshell to execute sleep 1; cat, and sets up a pipe to it.
  2. The main bash process executes echo foo. Since this doesn't fill up the pipe's buffer, the echo command terminates without blocking.
  3. The main bash process executes echo bar.
  4. The main bash process launches the command sleep 2.
  5. Meanwhile, the subshell is launching the command sleep 1.
  6. After about 1 second, sleep 1 returns in the subprocess. The subprocess proceeds to execute cat.
  7. cat copies its input to its output (which is displayed on the screen) and returns.
  8. The subshell has finished its job, it exits.
  9. After another second, sleep 2 returns. The main shell process finishes executing and you get to see the next prompt.
0
3

You don't need process substitution to get that effect. Try this:

( echo foo | cat & )

You'll get much the same result (without the bar; I'll leave that as an exercise) and for the same reason. In both cases, cat is started as a background task. In my line here, that's explicit. In the case of process substitution, it's also explicit -- it's a separate process attached to a name file descriptor -- but it's perhaps not as obvious.

The child process does not necessarily terminate before bash prints the next prompt. And since its output is buffered, it does not output anything until it terminates. At that point, bash has just printed $ on stderr, and now the background process exits after printing foo and a newline.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .