Marcus Rossel
Marcus Rossel

Reputation: 3258

Varying behavior when using named pipe with background process

I'm having a hard time understanding named pipes. I have the following script:

#!/bin/bash

function a_or_b {
   echo 'func begins'

   while true; do
      echo 'loop begins'

      read -s -n 1; echo $?
      case $REPLY in
         'a') return 0 ;;
         'b') return 1 ;;
      esac
   done

   echo 'func ends'
}

mkfifo pipe
a_or_b <pipe &

Now what I was expecting this script to do is:

  1. enter a_or_b and therefore print func begin
  2. enter the loop and therefore print loop begins
  3. read an EOF from stdin, and therefore from pipe(as I have not written anything to pipe) and therefore print 1 as $?
  4. not match on either case-clause and therefore go back to step 2

When running this script though I get no output and my terminal simply prints its next prompt.


If I redirect an echo into pipe before calling a_or_b:

...

mkfifo pipe
echo 'x' > pipe
a_or_b <pipe &

... the script doesn't stop running and I can keep entering characters (including a and 'b') in the terminal to no effect. So I have to end the script using ^C.


If I redirect an echo into pipe after calling a_or_b:

...

mkfifo pipe
a_or_b <pipe &
echo 'x' > pipe

... I get the following output:

func begins
loop begins
0
loop begins
0
loop begins
1
loop begins
1
loop begins
1

This is basically the behavior I expected from not echoing anything to pipe. The function begins, goes into the loop, reads the x and \n characters from the echo (corresponding to the two 0s in the output) and then keeps looping forever while failing to read any characters. And if I echo a or b into the pipe, the function ends.


What is causing all of these different behaviors?

Upvotes: 2

Views: 999

Answers (1)

John Kugelman
John Kugelman

Reputation: 361605

a_or_b <pipe &

Redirections are processed before starting commands. The shell blocks trying to open pipe for reading. From the mkfifo(3) man page:

Opening a FIFO for reading normally blocks until some other process opens the same FIFO for writing, and vice versa.

The shell can't proceed until another process opens the FIFO for writing. Only then will it finish setting up the redirection and actually call a_or_b.


echo 'x' > pipe
a_or_b <pipe &

Unfortunately this has the same, but inverse problem. The shell can't proceed past the > pipe redirection until another process opens the FIFO for reading. It never gets to the echo nor the second line that would read from the pipe.


a_or_b <pipe &
echo 'x' > pipe

Hopefully you can now see why this version works. The background shell tries to read from the pipe and blocks. The foreground shell, a separate process, writes to it. Aha! Two processes have opened the pipe, one for reading and one for writing. They can now both proceed. The birds sing and everybody is happy.

Upvotes: 4

Related Questions