4

This question already has an answer here:

I would like to send stdout to multiple commands, however I'm not sure how do I read from standard input within process substitution?

My attempts:

$ echo foo >(cat /dev/stdin) >(cat /dev/stdin)
foo /dev/fd/63 /dev/fd/62

$ echo foo >(cat -) >(cat -)
foo /dev/fd/63 /dev/fd/62

$ echo foo >(cat <&3) >(cat <&3) 3<&0
foo /dev/fd/63 /dev/fd/62
-bash: 3: Bad file descriptor
-bash: 3: Bad file descriptor

Alternative version of the same problem:

$ cat file | tee &>/dev/null >(cmd1 /dev/stdin) >(cmd2 /dev/stdin)

What's the right way of doing this?

marked as duplicate by mikeserv, kenorb, jimmij, Gilles bash Dec 6 '15 at 23:32

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • do you mean - from a terminal? – mikeserv Dec 6 '15 at 19:24
  • @mikeserv Yes, from terminal/script using bash shell. The - points to stdin. – kenorb Dec 6 '15 at 19:25
  • well... you kind of cant from a terminal - not like that. its to do with the process groups and what a controlling terminal will do with background process that try to read... there's an answer... just a minute. – mikeserv Dec 6 '15 at 19:27
  • I've seen that, but it doesn't solve the problem. The other question asking for the explanation why it doesn't work, however I'm looking for the solution which isn't provided there. Secondly it's other way round, there is <(cat), and I am looking for: >(cat -). – kenorb Dec 6 '15 at 19:37
  • 2
    What's wrong with just echo foo | tee >(cat) >(cat)? – muru Dec 6 '15 at 19:46
3

This reads from stdin:

echo foo | tee >(read line </dev/stdin; echo "internal $line")

You have to keep in mind that a process substitution acts "like" a file.
It could be used where a file is expected. The command tee expects to write to a file.
In that command we are being specific about the device to read from with: /dev/stdin. In that simple example, the /dev/stdin could be removed and that will work also:

echo foo | tee >(read line; echo "internal $line")

If I am understanding your need correctly, this will work:

$ echo foo | tee >(read a </dev/stdin; echo "a is $a") \
>(read b </dev/stdin; echo "b is $b") \
>(read c </dev/stdin; echo "c is $c")
foo
a is foo
c is foo
b is foo

I omitted the PS2 prompt to reduce confusion. Note that each Process Substitution replaces the use of a file (as: tee FILE FILE ....).

The read does not have to be the first command.

$ echo foo > >(echo "hello" | read b; read a </dev/stdin; echo "a is $a") 
a is foo

Note that here the "Process Substitution" needs a redirection,
that is the reason of the two > >( idiom.

A simple echo, will only print the number of the fd used (the name of the file):

$ echo >(echo "hello")
/dev/fd/63
hello

It is similar to:

$ echo "$file"
filename

Whereas this is a very different idiom:

$ echo > "$file"

Not the answer you're looking for? Browse other questions tagged or ask your own question.