I have a short c programme that outputs 3 lines then reads from stdin, and I would like to use a shell script to interact with it as follows:
while read line; do ...; done <$out_
if [ "$count" == 2 ]; then input=$line; fi
echo $input >$in_
I'm using named pipes in the shell script to redirect the output and input of the c programme:
./iotest >$out_ <$in_ &
but I'm not getting the results I'd expect.
After creating the pipes out_
and in_
with mkfifo
, I have a while read
loop but it seems to lead to a deadlock. Nothing is read, and the shell script gets stuck on the read command.
I tried adding cat >$in_ &
before launching iotest
, as proposed here, to open in_
for writing without actually writing anything. This unblocks the read but doesn't let me input to in_
(my c programme acts as if I'd written \x00 to stdin, and the echo $input >$in_
command hangs).
I've read here that read blocks if there is no writer, and wihtout cat >$in_ &
I have iotest
writing to out_
, and read
reading from it, I'm trying to redirect in_
to iotest
without writing anything to it from the start.
But why adding the cat command makes the c programme fail at reading, I don't know. The same happens if I exchange cat >$in_ &
with while true; do :; done >$in_&
or exec 3>$in_ &
(exec 3>$in_
, as proposed here, also hangs).
This is my c code:
$ cat iotest.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main (int argc, char* argv[])
{
char A[9] = "";
printf("hello. please send me the following line:\n");
printf("input me\n");
printf("now send me the line:\n");
int ret = read(0,A,8);
fprintf(stderr,"read returned %i\n",ret);
if(ret != -1) {
printf("You sent: %s\n", A);
if (strcmp(A,"input me") == 0) {
printf("correct!\n");
} else {
printf("wrong line.\n");
}
} else {
printf("read failed.\n");
}
return 0;
}
and this is the shell script I've tested:
$ cat runiotest.sh
#!/bin/bash
in_=/tmp/testpipein
out_=/tmp/testpipeout
mkfifo $in_
mkfifo $out_
count=0
input=""
./iotest >$out_ <$in_ &
while read line
do
count=$((count+1))
echo "line number $count:"
echo $line
if [ "$count" == 2 ]
then
input=$line
fi
if [ "$count" == 3 ]
then
echo "$input" >$in_
fi
done < $out_
rm -f $in_
rm -f $out_
I've tested this on 32-bit debian and 64-bit ubuntu, compiling with gcc, and executing the shell script with bash.
without the added cat command:
$ /bin/bash/ ./runiotest.sh
(hangs)
with added cat >$in_ &
:
$ /bin/bash ./runiotest.sh
read returned 0
line number 1:
hello. please send me the following line:
line number 2:
input me
line number 3:
now send me the line:
line number 4:
You sent:
line number 5:
wrong line.
So my question is: What is wrong with my shell script, and how can I modify it to read then write to my c programme? I can't change the c code itself but can fiddle with the shell script.
Thank you for any help!
The problem was the buffering of the c programme output. Removing the buffering with stdbuf
solved it:
cat >$in_ &
stdbuf -o0 ./iotest <$out_ >$in_ &
With this change, the output is as it should:
$ /bin/bash ./runiotest.sh
line number 1
hello. please send me the following line:
line number 2
input me
line number 3
now send me the line:
read returned 8
line number 4
You sent: input me
line number 5
correct!
Just to add, using stdbuf doesn't work if the executable has the setuid bit set. In this case, you can use unbuffer
from the expect utility.