How to communicate easily between process in a shell script?

We can use fifo:

$ mkfifo my_fifo
$ cat my_fifo &
[1] 7266
$ echo foo > my_fifo
foo
[1]  + done       cat my_fifo

As you can see, cat finish as soon as something is written in fifo. We can work around this behavior by using tail -f instead of cat. In this case, tail -f will reopen fifo.

We may use two fifos to communicate with ssh for example. But we will have same problem than with cat :

$ mkfifo fifo1 fifo2
$ ssh host >fifo1 <fifo2 &
[1] 7266
$ echo ls > fifo2
[1]  + done       ssh host > fifo1 < fifo2

The other problem is writing in a fifo is blocking while there no process in the other end:

 $ echo foo > my_fifo
 [block until someone read my_fifo]

A good way would be to keep the file open during all the time.

On another hand, Your shell provide a way to open file descriptors and link them with a file:

$ exec 5>file
$ exec 6<file

It could be useful to read a file line by line:

 $ exec 6<file
 $ while read A; do
    [[ $A == "}" ]] && break
 done <&6
 $ read A <&6
 $ echo $A
 Line just after first closed brace

(You can also use syntax read -u 6 A)

Opening file descriptor in shell is really useful when used with fifo, because it will keep fifo opened during all session:

$ mkfifo my_fifo
$ exec 5<>my_fifo
$ perl -pe '$d=`date`; chop $d; s/^(.*)$/$d $1/' <&5 &
[1] 7266
$ echo foo >&5
Wed Aug 24 22:01:47 CEST 2011 toto

We can now use fifos with file descriptors to communicate correctly with a ssh session:

$ mkfifo fifo1 fifo2
$ exec 5<>fifo1 6<>fifo2
$ ssh sysmic.org <&5 >&6 &
[1] 7266
$ echo ls >&5
$ cat <&6
file1
file2

It is a little restrictive to have to create two fifo. Shell provide a function called coproc. It allows to execute a command in background while communicating with it using file descriptor (equivalent of popen2 in perl). The file descriptor is accessible with &p (following code is for zsh):

$ coproc cat
[1] 1272
$ exec 5>&p
$ exec 6<&p
$ coproc cat -n
[2] 1291
$ exec 7>&p
$ exec 8<&p
$ print -u 5 foo
$ print -u 7 bar
$ read -u 6 A
$ read -u 8 B
$ echo $A
foo
$ echo $B
1       bar

Note that equivalent code under bash give a warning, but it also work.

Finally, you can allocate file descriptor dynamically using {NAME} syntax:

$ coproc cat
[1] 1272
$ exec {CAT_IN}>&p
$ exec {CAT_OUT}<&p
$ coproc cat -n
[2] 1291
$ exec {CATN_IN}>&p
$ exec {CATN_OUT}<&p
$ print foo >&{CAT_IN}
$ print bar >&{CATN_IN}
$ read A <&{CAT_OUT}
$ read B <&{CATN_OUT}
$ echo $A
foo
$ echo $B
1       bar