I am trying to write a script to initiate a daemon which while pipe its output and input via two named pipes $FIFO.out
and $FIFO.in
respectively. What I wish to do is to give the client process the ability to talk with the daemon whenever it wants by using the named pipes.
Here is what I have already implemented: (The daemon is a qalc process)
#!/bin/sh
# Daemon script
FIFO="/tmp/qcalc"
mkfifo "$FIFO.out" "$FIFO.in"
exec qalc -t -u8 <"$FIFO.in" >"$FIFO.out"
#!/bin/sh
# Client script
FIFO="/tmp/qcalc"
talk() {
echo "$1" >"$FIFO.in"
# I'm not interested in the first two lines
read -r _ <"$FIFO.out"
read -r _ <"$FIFO.out"
read -r result <"$FIFO.out"
echo "$result"
}
talk 1
talk 2
The problem with this implementation is that I can only talk to the daemon once, and after that the client just freezes at the blocking read. Here is how it looks in the shell:
$ ./daemon &
[2] 57848
$ ./client
1
[2] + done ./daemon
^C./client: 13: cannot open /tmp/qcalc.out: Interrupted system call
This was a nice edge case when using named pipes and I learned a lot trying to get this system to work:
Firstly, here's the final implementation I wrote:
#!/bin/sh
# Daemon script
FIFO="/tmp/qcalc"
mkfifo "$FIFO.in" "$FIFO.out"
qalc -t -u8 <"$FIFO.in" >"$FIFO.out" &
qalcPID="$!"
exec 3>"$FIFO.in" # Dummy writer to keep the pipe alive
wait "$qalcPID"
exec 3>&-
#!/bin/sh
# Client script
FIFO="/tmp/qcalc"
echo "$1" >"$FIFO.in"
exec 3<"$FIFO.out"
read -r _ <&3
read -r _ <&3
read -r result <&3
read -r _ <&3
echo "$result"
exec 3<&-
What I understood from another SE question about pipes was that reading end pipe will get an EOF
or it will be closed if the writing end is not connected.
As @PCDSandwichMan correctly pointed out in his answer, my previous attempt failed because qalc
was closing when it detected $FIFO.in
closing. Therefore, to keep the pipe alive, I added a dummy writer after qalc
is launched.
qalc -t -u8 <"$FIFO.in" >"$FIFO.out" &
exec 3>"$FIFO.in" # Dummy writer to keep the pipe alive
The writer has to be added after a reader for the pipe is established so I launch qalc
as a background process and then do it.