4

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)?

Community
  • 1
  • 1
scherand
  • 2,298
  • 20
  • 27
  • Try putting a newline after `[C]`, i.e print `"[C] \n";` – 123 Jul 23 '15 at 08:27
  • It is probably doable with your coproc approach, but as i don't have the answer at hand i can see a particularly ugly workaround which would be to insert newlines after the missing one you know (ie: `<$writepipe [call perl program] | sed -u 's/ ? \[C\] /&\n/g' &> $readpipe &` – bufh Jul 23 '15 at 08:46
  • Can you use python? If so, how about replacing the `[call perl program]` with a python wrapper that gives pseudo-terminals around it? `/usr/bin/env python -c "import pty, sys; pty.spawn(sys.argv[1:])" /path/to/program.pl < $writepipe &> $readpipe` – bishop Aug 21 '15 at 20:32
  • 1
    Have you considered EmPTY? See http://empty.sourceforge.net/ – Charles Duffy Aug 21 '15 at 20:43
  • This, to me, looks like a buffering issue in your Perl script. Try putting `$|++` at the top of your Perl script. I know this is old, but it would be interesting if that's the fix to the issue you're seeing. – Jim Jul 29 '16 at 22:00

1 Answers1

3

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]
 ***
pasaba por aqui
  • 3,446
  • 16
  • 40
  • I see (and like) your basic idea, I believe. Unfortunately it is not working in my case. I was not yet able to figure out why. It seems that my script never reads the '?' for some reason (or never sees a '\0'?). – scherand Sep 01 '15 at 14:21
  • @scherand: Could you edit the original question, adding the new script version and its output? It could be laso interesting add a echo ":$c:" as first statement in the while loop, to see which characters are handled. – pasaba por aqui Sep 01 '15 at 18:16