Reputation: 2358
I am writing a bash script that should interact (interactively) with an existing (perl) program. Unfortunately I cannot touch the existing perl program nor can I use expect.
Currently the script works along the lines of this stackoverflow answer Is it possible to make a bash shell script interact with another command line program?
The problem is (read: seems to be) that the perl program does not always send a <newline>
before asking for input. This means that bash's while ... read
on the named pipe does not "get" (read: display) the perl program's output because it keeps waiting for more. At least that is how I understand it.
So basically the perl program is waiting for input but the user does not know because nothing is on the screen.
So what I do in the bash script is about
#!/bin/bash
mkfifo $readpipe
mkfifo $writepipe
[call perl program] < $writepipe &> $readpipe &
exec {FDW}>$writepipe
exec {FDR}<$readpipe
...
while IFS= read -r L
do
echo "$L"
done < $readpipe
That works, unless the perl program is doing something like
print "\n";
print "Choose action:\n";
print "[A]: Action A [B]: Action B\n";
print " [C]: cancel\n";
print " ? ";
print "[C] ";
local $SIG{INT} = 'IGNORE';
$userin = <STDIN> || ''; chomp $userin;
print "\n";
Then the bash script only "sees"
Choose action:
[A]: Action A [B]: Action B
[C]: cancel
but not the
? [C]
This is not the most problematic case, but the one that is easiest to describe.
Is there a way to make sure the ? [C]
is printed as well (I played around with cat <$readpipe &
but that did not really work)?
Or is there a better approach all together (given the limitation that I cannot modify the perl program nor can I use expect
)?
Upvotes: 4
Views: 2574
Reputation: 3539
Use read -N1
.
Lets try with following example: interact with a program that sends a prompt (not ended by newline), our system must send some command, receive the echo of the command sent. That is, the total output of the child process is:
$ cat example
prompt> command1
prompt> command2
The script could be:
#!/bin/bash
#
cat example | while IFS=$'\0' read -N1 c; do
case "$c" in
">")
echo "received prompt: $buf"
# here, sent some command
buf=""
;;
*)
if [ "$c" == $'\n' ]; then
echo "received command: $buf"
# here, process the command echo
buf=""
else
buf="$buf$c"
fi
;;
esac
done
that produces following output:
received prompt: prompt
received command: command1
received prompt: prompt
received command: command2
This second example is more near to the original question:
$ cat example
Choose action:
[A]: Action A [B]: Action B
[C]: cancel
? [C]
script is now:
#!/bin/bash
#
while IFS=$'\0' read -N1 c; do
case "$c" in
'?')
echo "*** received prompt after: $buf$c ***"
echo '*** send C as option ***'
buf=""
;;
*)
buf="$buf$c"
;;
esac
done < example
echo "*** final buffer is: $buf ***"
and the result is:
*** received prompt after:
Choose action:[A]: Action A [B]: Action B
[C]: cancel
? ***
*** send C as option ***
*** final buffer is: [C]
***
Upvotes: 3