I would like to talk to an external program (midish) from
perl using Expect (or Expect::Simple). I want to spawn this program once, keeping the child process loaded while I do other stuff. AFAIK Midish is well-behaved, using STDIN and STDOUT for I/O. However I'm having a hard time getting a reponse reliably after sending a command.
Update 1: I will investigate approaches other than Expect.
Update 2: Zentara's solution using IPC::Open3 is *way* simpler.
My first experiment was a simple command loop:
- get a prompt ("+ready")
- send a command
- get a reply
I thought I would try with Expect::Simple,
however some buffering or command-ordering problems arise.
------ test-expect-simple ----
use Modern::Perl;
use Expect::Simple;
$| = 1;
my $obj = Expect::Simple->new({
Cmd => [ midish => '-v'],
Prompt => '+ready',
DisconnectCmd => 'exit',
Verbose => 0,
Debug => 0,
Timeout => 100
});
sub prompt { print "midish> " }
while ( prompt(), my $cmd = <STDIN> ){
exit if $cmd =~ 'quit';
chomp $cmd;
$cmd .= "\r";
$obj->send( $cmd );
my @lines = split "\n", $obj->before;
splice(@lines, 0, 2); # throw away the first two lines
say join "\n", @lines;
}
__END__
$perl test-expect-simple
midish> asdf
asdf: no such proc
midish> 1234
midish> ffff
3.5: statement or proc definition expected
midish> ^C
Comment: I'm not getting the full message after
sending a command. Maybe it's a problem with
Expect::Simple.
So here is the next try, a command loop using Expect.
------ test-expect-loop -----
use Expect;
use Modern::Perl;
my $exp = Expect->spawn("midish","-v")
or die "Couldn't start program: $!\n";
# don't copy program output to STDOUT
$exp->log_stdout(0);
$exp->expect(1, '+ready', \&do_cmd);
sub do_cmd {
map{say "midish: $_"} split "\n", $exp->before;
print "enter command >> ";
my $cmd = <STDIN>;
$exp->send($cmd);
exp_continue;
}
$exp->soft_close();
__END__
program output:
$ perl test-expect-loop
enter command >> adsfsd
midish:
midish: adsfsd
midish: adsfsd: no such proc
enter command >> 1234
midish:
midish: 1234
midish: 2.5: statement or proc definition expected
enter command >> ffff
midish:
midish: ffff
midish: ffff: no such proc
enter command >> exit
$
Good, I'm getting output after every command.
But my application is structured more like this:
call_to_expect($one_command);
execute_other_code();
call_to_expect($another_command);
------ test-expect-unrolled -----
use Expect;
use Modern::Perl;
my $exp = Expect->spawn("midish","-v")
or die "Couldn't start program: $!\n";
# don't copy program output to STDOUT
$exp->log_stdout(0);
$exp->expect(1, '+ready', \&do_cmd);
print_output();
$exp->expect(1, '+ready', \&do_cmd);
print_output();
$exp->expect(1, '+ready', \&do_cmd);
print_output();
sub do_cmd {
print "enter command >> ";
my $cmd = <STDIN>;
$exp->send($cmd);
}
sub print_output {
map{say "midish: $_"} split "\n", $exp->before;
}
$exp->soft_close();
__END__
program output:
$ perl test-expect-unrolled
enter command >> asdf
enter command >> 1234
midish:
midish: asdf
midish: asdf: no such proc
enter command >> ffff
midish:
midish: 1234
midish: 2.5: statement or proc definition expected
<hang>
^C
$
So, I still have a problem getting a one-to-one
correspondence between the command string and the result
string.
Any ideas how I could make one call to send a command and get a response, in a way that I can repeat indefinitely?
Thanks for reading!