Problem with pipes. Pipe terminates when reader done

fifopipe

I am on OSX, using bash, trying to make sense of pipes. I wish to let a program communicate in two directions with a bash shell. I want to set this up in such a way that this is always the same shell, so that I can cd to some directory and bash will remember (rather than using a new bash shell all the time).

What I have tried so far is this. From a new terminal (A), do

mkdir /tmp/IOdir
cd /tmp/IOdir
mkfifo pToB
mkfifo bToP
tail -f -1 pToB | bash >> bToP

Then, to test this connection, I can do, from a new terminal (B)

cd /tmp/IOdir
echo echo hello > pToB

and from a third terminal (C)

cd /tmp/IOdir
(read myline &&  echo $myline) < bToP

This behaves how I want. The same bash shell stays active, and the output comes through on the other side. Call this state of affairs X, so that I can refer later to it.

Onward from state X

However, now, from this state X, we cannot do the same thing again. That is, if we do from terminal (B)

echo echo hello > pToB

and then from terminal C

(read myline &&  echo $myline) < bToP

Then nothing comes through in terminal C. Moreover, if we again do, from terminal B

echo echo hello > pToB

The bash shell closes.

What I could have done in state X was first do, from terminal C

(read myline &&  echo $myline) < bToP

and then from terminal B

echo echo hello > pToB

In this case hello comes through at terminal C, and it seems like we are in state X again. So we can basically repeat this forever. Now this might seem sufficient for two way communication, but my program is such that if it requests a new line like this

(read myline &&  echo $myline)

and there is no new line, it will "hang" (just like bash, in fact I mean to use a call to bash in the program). It is therefore not able to send input to pToB after that and there is nothing I can do.

Questions

Is there a way to set this up without doing too much programming in C? Is there a way to do this more elegantly without using two named pipes? What is causing the pipe to close in one scenario, and not in the other?

Edits

From this page on wikipedia, we have

Full-duplex (two-way) communication normally requires two anonymous pipes.

On one hand, it seems at least I have the right number of pipes. On the other, I am using named pipes, not anonymous ones. So maybe this will be hard/impossible.

Furthermore mkfifo gnu/linux source is likely defined in terms of mknod gnu/linux source, which is also a unix command. But I'm not sure if much can be learned from that.

Here is an introduction to pipes in C, including the linux source of pipe. Maybe that can tell us why a pipe gets closed, if that is indeed what happens.

Here is a related question about preventing that fifos get closed. I tried tying the pipes to background sleeping processes as was done in an answer tere but that didn't help.

Best Answer

As for the cause, use strace.

tail -f | strace bash >> foo

The second echo echo hello > pToB gives me then this:

rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
read(0, "e", 1)                         = 1
read(0, "c", 1)                         = 1
read(0, "h", 1)                         = 1
read(0, "o", 1)                         = 1
read(0, " ", 1)                         = 1
read(0, "h", 1)                         = 1
read(0, "e", 1)                         = 1
read(0, "l", 1)                         = 1
read(0, "l", 1)                         = 1
read(0, "o", 1)                         = 1
read(0, "\n", 1)                        = 1
write(1, "hello\n", 6)                  = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=3299, si_uid=1000} ---
+++ killed by SIGPIPE +++

So, the second time it tries to write hello\n, it gets a broken pipe error; that's why you can't read hello (it was never written), and bash quits so that's the end of it.

You'd have to use something that keeps the pipe open, I guess.

How about this?

(while read myline; do echo $myline; done) < pToP

For more background information, man 7 pipe may be relevant, it describes the various error cases around pipes.