1

Consider the below script

#!/usr/bin/bash

chan=/tmp/pipe.$$
mkfifo $chan
{
    for x in a b c d e
    do
        echo $x > $chan
    done
} &
for y in 1 2 3 4 5
do
    x=$(cat $chan)
    echo "var $x = $y ;"
done
rm $chan

Does not seem to work as expected. E.g.

❯ ./testpipe.sh
var a
b
c = 1 ;
^C

Does the echo not supposed to wait if there are no readers for the fifo? When I do this in terminal, it hangs as expected

❯ mkfifo /tmp/pipe
❯ echo 1 > /tmp/pipe
^C

Why does it behave differently in the script?


Update

I changed to use fds instead of opening every time.

#!/usr/bin/bash
chan=/tmp/pipe.$$
mkfifo $chan    
{
    exec 5>"$chan"
    for x in a "ba ba ba baanannna" c d e
    do
        echo $x >&5
    done
} &

exec 6<"$chan"
for y in 1 2 3 4 5
do
    read -u 6 x
    echo "var $x = $y ;"
done
rm $chan

Not sure I fixed it or just made it more rare for the race to occur.

❯ ./testpipe.sh
var a = 1 ;
var ba ba ba baanannna = 2 ;
var c = 3 ;
var d = 4 ;
var e = 5 ;

Update 2

Don't need a fifo at all. Just proc substitution is enough to run something in background and read from it when required. Tested adding sleep before and after in both producer and consumers. Seems to work fine. Likely no race.

#!/usr/bin/bash

produce()
{
    for x in a "ba ba ba   baanannna" "more    space" d e
    do
        echo "$x"
    done
}

exec {fd}< <(produce)
for y in 1 2 3 4 5
do
    read -u $fd x
    echo "var $x = $y ;"
done

1 Answer 1

1
Running in background shell -- running in foreground shell

echo a waits
                        cat opens read side
                        cat is maybe faulting in some pages or maybe not just picked 
                        but for whatever reason doesn't immediately call read
echo a completes
echo b doesn't wait (read side already open) and completes
echo c ditto
                        cat calls read, gets a NL b NL c NL and writes them 
                        (but the shell's $(...) that reads them drops the final NL)
echo d probably doesn't wait, but might if cat has close'd already (race condition)

If you make it $(cat <$chan) instead you'll probably get all a-e because then subshell opens read side before exec'ing cat before reading, increasing the delay. OTOH if you use command echo or /bin/echo to slow down the producer, you'll probably only get a.

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.