In a previous question I asked how to write to a program's stdin. This question builds off of that one. Now I am asking "how do I parse my called program's output".
The fact that I'm calling a program I can edit (Sum.sh) in this example is irrelevant to the question. I want this behavior in the event that I do not have control over the implementation of the program I am calling. I want to "replace" the physical human that a program expects and drive it via a script.
Given two scripts
Sum.sh:
#!/bin/bash
sum=0
inp=0
while true;
do
printf "Sum: %d\n" $sum
printf "Give me an integer: "
read inp
if [ "$inp" == 0 ]; then
exit
fi
sum=$((sum+inp))
done
And driver.sh:
#!/bin/bash
rm -f /tmp/sumpipe
mkfifo /tmp/sumpipe
./Sum.sh < /tmp/sumpipe &
echo 2 > /tmp/sumpipe
echo 5 > /tmp/sumpipe
echo 0 > /tmp/sumpipe
sleep 1 # this is required for some reason or the script hangs
And I run driver.sh I get the following:
$ ./driver.sh
Sum: 0
Give me an integer: Sum: 2
Give me an integer: Sum: 7
Give me an integer: $
The formatting of the terminal output is not important to this question.
What I want to do is parse said output in order to make decisions in driver.sh. In the following psuedo code of driver.sh I try to show what I want to do:
#!/bin/bash
rm -f /tmp/sumpipe
mkfifo /tmp/sumpipe
output=$(./Sum.sh < /tmp/sumpipe &)
while true; do
if [ "$output" != empty ]; then
(make some decision based on what output is and echo back to my process if necessary)
fi
done
Unfortunately, $output
never contains anything, and I have tried many versions of abusing read and piping and none of them have worked for me in this application. How can I redirect my backgrounded program's stdout so I can parse it?
I attempted OrangesV's proposed solution (and with deference to William Pursell's suggestion about the out buffer being too empty) with the following:
#!/bin/bash
rm -f /tmp/sumpipe
mkfifo /tmp/sumpipe
while read output; do
echo "2347823" > /tmp/sumpipe
if [ -z $output ]; then
continue
else
echo "got it!"
echo $output
sleep 1
fi
done < <(./Sum.sh < /tmp/sumpipe &)
Unfortunately, "got it!" is never echoed, so it seems it didn't work.
In case it's relevant, my bash version:
$ bash --version
GNU bash, version 4.4.0(7)-rc2 (x86_64-unknown-linux-gnu)
There is now a partial solution. Diego Torres Milano's answer does work. However, my stdio is being buffered so that I won't get anything till stdio has at least 4096 bytes in it. I can force output with the following version of driver.sh:
#!/bin/bash
inpipe=/tmp/sumpipei
outpipe=/tmp/sumpipeo
rm -f $inpipe
rm -f $outpipe
mkfifo $inpipe
mkfifo $outpipe
./Sum.sh < $inpipe > $outpipe &
count=0
while true;
do
while read line
do
echo "line=$line"
done < $outpipe &
echo 2 > $inpipe
done
I tried all sorts of invocations of stdbuf
(like: stdbuf -o0 ./Sum.sh <$inpipe > $outpipe &
). stdbuf
does not apparently work in this situation to force off buffering because, according to the GNU coreutils manual on stdbuf
for command
in stdbuf option… command
:
command must start with the name of a program that does ... not adjust the buffering of its standard streams (note the program tee is not in this category).
Which is unfortunately exactly what I'm doing here.
I'm marking Diego's answer as correct, even though it doesn't work until the stdio buffer issue is fixed. I'll make my next question to address the stdio buffering problem. I can almost taste the prize here.