19

I have two processes which I need to connect like this:

proc1 -- sends output to proc2 proc2 -- sends output to proc1

So far, all pipe examples are of this kind: proc1 | proc2

That's nice, but how do I make the output of proc2 go to proc1?

A bash example would be nice. A Windows shell example would be great :)

Thanks in advance, Adrian.

Adding more details:

The system is expected to work as client-server system in which the client works with the server in a request-response interaction model. The interaction ends when the client has no more requests.

Interaction example: client: request1; server: response1; client: request2; server: response2; . . . . client: closeRequest; server: closeApproved;

At this point the server exits following a client exit. End of example.

It seems that a solution like (assuming pipe is available) client < pipe | server > pipe would not work (please correct me) because in this arrangement the client produces one big request, the shell pipes this big request to the server, then the server produces one big response, and finally the shell pipes this big response to the client.

Adrian Herscu
  • 738
  • 1
  • 6
  • 19
  • 3
    [In bash, it can be solved with named pipes](http://stackoverflow.com/questions/40244/how-to-make-a-pipe-loop-in-bash). Not so sure about Windows... – Aasmund Eldhuset Feb 26 '11 at 19:46

5 Answers5

19

It looks like a bash coprocess may be what you want. Look up the coproc reserved word in the bash manual.


(Edit: adding simple usage scheme)

It works like this:

# start first process as a coprocess to the current shell
coproc proc1

# now ${COPROC[0]} contains the number of an open (input) file descriptor
# connected to the output of proc1, and ${COPROC[1]} the number of an
# open (output) file descriptor connected to the input of proc1.


# start second process, connecting its input- and outputstreams
# to the output- and inputstreams of the first process
proc2 <&${COPROC[0]} >&${COPROC[1]}

# wait on the first process to finish.
wait $COPROC_PID

If you may have multiple coprocesses, give your process a name like this:

coproc NAME {
    proc1
}

Then you can use NAME wherever COPROC was used before.


Here is a complete example program using a ping function as proc1 and proc2:

#!/bin/bash
#
# Example program using a bash coprocess to run two processes
# with their input/output streams 
#


#
# A function which reads lines of input and
# writes them back to standard output with the
# first char cut off, waiting 5s inbetween.
#
# It finishes whenever an empty line is read or written,
# or at end-of-file.
#
# The parameter $1 is used in debugging output on stderr.
#
function ping ()
{
    while read 
    do
        local sending
        echo "ping $1: received '$REPLY'" >&2
        [[ -n $REPLY ]] || break
        sleep 5s
        sending=${REPLY:1}
        echo "ping $1: sending '$sending'"  >&2
        echo $sending
        [[ -n $sending ]] || break
    done
    echo "ping $1: end" >&2
}

#
# Start first ping process as a coprocess with name 'p1'.
#

coproc p1 {
    ping 1
}

# send some initial data to p1. (Not needed if one of the processes
# starts writing before first reading.)
echo "Hello World" >&${p1[1]}
sleep 2.5s

#
# Run second ping process, connecting its default input/output
# to the default output/input of p1.
# 
ping 2 <&${p1[0]} >&${p1[1]}

# wait for the coprocess to finish too.
wait $p1_PID

It uses two invocations of a shell function instead of external programs, but it would work with such programs too. Here is the output (on stderr):

ping 1: received 'Hello World'
ping 1: sending 'ello World'
ping 2: received 'ello World'
ping 2: sending 'llo World'
ping 1: received 'llo World'
ping 1: sending 'lo World'
ping 2: received 'lo World'
ping 2: sending 'o World'
ping 1: received 'o World'
ping 1: sending ' World'
ping 2: received 'World'
ping 2: sending 'orld'
ping 1: received 'orld'
ping 1: sending 'rld'
ping 2: received 'rld'
ping 2: sending 'ld'
ping 1: received 'ld'
ping 1: sending 'd'
ping 2: received 'd'
ping 2: sending ''
ping 2: end
ping 1: received ''
ping 1: end
Paŭlo Ebermann
  • 73,284
  • 20
  • 146
  • 210
  • If needed, more info will follow after I have slept a bit more. – Paŭlo Ebermann Feb 27 '11 at 02:45
  • Hmmm... That's too complex for a Bash newbie like me :( What's the added value over a named pipe? (please, see previous answer) – Adrian Herscu Feb 27 '11 at 18:39
  • The advantage would be that it works on systems without named pipe support, if you have `bash` there, and that the two commands don't have to know about the redirection (while you could use redirection with fifos, too). Note that the example mainly looks so complicated because of my `ping` function - instead you would have simply your program calls (I'll amend my answer). – Paŭlo Ebermann Feb 27 '11 at 19:07
11

here is a bash example using echo to provide some initial input and a named pipe to allow for the feedback loop:

mkfifo fifo
echo "fifo forever" | cat - fifo | tee fifo
  • proc1 (cat) takes input from stdin and fifo
  • proc2 (tee) passes output to stdout and fifo
codecraft
  • 1,163
  • 9
  • 11
  • That seems close enough. However, I am far from being a Bash expert... What's the meaning of the '-' (minus) on the cat process? Anyway, can it be simplified to: `mkfifo fifo; proc1 fifo | proc2 fifo`? – Adrian Herscu Feb 27 '11 at 18:37
  • the **-** means: take the input from the command line. This is a convention among gnu command line tools (http://www.gnu.org/software/coreutils/manual/html_node/Common-options.html) not a peculiarity of the shell. – codecraft Feb 27 '11 at 18:55
  • 1
    and yes `mkfifo fifo; proc1 fifo | proc2 fifo` would be correct, assuming that proc1 expects an input and proc2 an output file handle as its argument. – codecraft Feb 27 '11 at 19:00
  • 3
    actually, `proc1 < fifo | proc2 > fifo` should work for processes which don't know anything about fifos. (And you shouldn't forget to delete the fifo afterwards.) – Paŭlo Ebermann Feb 27 '11 at 19:21
  • 1
    @codecraft the `-` is a way to specify "standard in" instead of a filename in many gnu command line tools. "Take the input from the command line" is a strange way to say that. – rjmunro Oct 02 '13 at 12:27
6

This is an example showing what you want, with two processes that both take one argument:

mkfifo fifo
./process1 argument1 < fifo | ./process2 argument1 > fifo

First we create a named pipe called fifo in the current directory. Then we execute process1 with fifo as input, whose output will go through the anonymous pipe | to the input of process2, whose output will go to fifo, closing the loop.

After exiting both processes you should delete the pipe, just the same way you delete a regular file:

rm fifo
cprcrack
  • 17,118
  • 7
  • 88
  • 91
1
#!/bin/bash

fifo=$(/bin/mktemp -u)
/usr/bin/mkfifo $fifo

# register resources cleanup trap
trap "/bin/unlink $fifo" INT TERM EXIT

# start a background shell redirecting its output to the pipe
(for ((i=0; i < 5; ++i)); do echo "pam pam, i = $i"; sleep 1; done ) 1> $fifo&

# read the output of the background shell from the pipe
while read line 0< $fifo; do
  echo "param $line"
done

# wait for child process to terminate
wait
bobah
  • 18,364
  • 2
  • 37
  • 70
-2

Most shells make this hard to do, for good reason - it's very easy to get your programs deadlocked, where each is waiting for input from the other. Can you tell us more about what information you're trying to pass around? If you are just trying to make an RPC-like system, you should probably use a library that's designed for that.

Quentin Smith
  • 214
  • 1
  • 6
  • I am designing a testing system for my company's product. – Adrian Herscu Feb 27 '11 at 18:21
  • Ooops... Hitting enter here, adds a comment... Ok. The test runner sends commands to a proxy (that knows how to talk with our hardware). I am trying to separate things as much as possible. I am aware of other IPC methods, but all of them require linking with some specific libraries and I do not want to enforce something like this on our development team. Sending strings through standard I/O streams seems the lowest common denominator. – Adrian Herscu Feb 27 '11 at 18:27