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.


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?


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.

  • Welcome to unix.stackexchange.com! Thank you for including lots of details in your question. Make sure to stop by the help page. – user26112 Jul 4 '13 at 15:32
  • @EvanTeitelman thanks for the warm welcome. I looked at the help page. I also edited my question a bit. Do you know if it is possible to add invisible/comment text to a question? For example, I was not allowed to put more links in here. I figured maybe somebody could edit and put them in for me. Anyway it does not matter in this case, I don't think people care about the source of mknod :P. – Trooper Jul 4 '13 at 17:55
  • You can use HTML-style comments: <!-- comment text -->. If you add the links as comments, I'll make them real links for you. – user26112 Jul 4 '13 at 17:58
  • @EvanTeitelman thank you. I have edited the question, but maybe the other brackets ( () and [] ) we interfering. It would be nice if it looked like (gnu/linux source), with the brackets. – Trooper Jul 4 '13 at 18:13
  • @EvanTeitelman this is fine too, thanks again :). – Trooper Jul 4 '13 at 18:13

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.


As an addendum to frostschutz answer, consider the following table

enter image description here

The first time we try to send hello into the bToP, it gets blocked until there is also a reader for the fifo. The second time we, the reader sends back a SIGPIPE signal, which terminates bash. Even if we could ignore that, we would probably run into the EPIPE error after that.

I think what I am trying to do is impossible. My application cannot "read and write at the same time". I think I will have to make an intermediate program capable of keeping both sides of the pipe open.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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