我正在寻找以下方法:
我找到了
Expect
,
和IPC
,但到目前为止我发现的所有内容似乎都在运行的背景下 - >写 - >阅读 - >退出,我需要这个外部应用程序继续运行,Perl脚本继续响应,直到我杀死它们。
答案 0 :(得分:0)
我需要这个外部应用程序继续运行并且perl脚本继续响应,直到我杀死它们。
这是一个有趣的问题 - 解决这个问题的一种可能方法是使用像POE
这样的事件循环框架。虽然它有一点学习曲线(但请参阅cookbook),但我发现它非常适合像专业网络服务器这样的东西,或者像你的情况那样 - 在控制台上保持互动而其他东西(网络连接,串口等)也会被处理。
use warnings;
use strict;
sub POE::Kernel::ASSERT_DEFAULT () { return 1 }
use POE qw/ Wheel::ReadWrite Wheel::Run /;
my @CHILD = ('perl', '-wMstrict', '-nle',
q{ $|=1; print uc; sleep 5; print lc });
POE::Session->create( inline_states => {
_start => sub {
$poe_kernel->alias_set('console_handler');
$_[HEAP]{console} = POE::Wheel::ReadWrite->new(
InputHandle => \*STDIN, OutputHandle => \*STDOUT,
InputEvent => 'console_input', ErrorEvent => 'console_error' );
},
console_input => sub {
my ($heap, $input) = @_[HEAP,ARG0];
if ($input=~/^(?:quit|exit)$/i) {
$poe_kernel->post(signal_handler => 'signal_shutdown',
'user request');
}
elsif ($input=~/^send\s+(.*)$/i) {
$poe_kernel->post(child_handler => 'child_stdin', $1);
}
else {
$heap->{console}->put('Unknown command - try "send ..."');
}
},
console_output => sub {
my ($heap, $output) = @_[HEAP,ARG0];
if (defined $heap->{console})
{ $heap->{console}->put($output) }
else # assume we're shut down, don't need to go through the wheel
{ print $output, "\n" }
},
console_error => sub {
my ($op, $errnum, $errstr) = @_[ARG0..ARG2];
$poe_kernel->post(signal_handler => 'signal_shutdown',
$op eq 'read' && $errnum==0 ? 'EOF'
: "console error (op $op error $errnum: $errstr)" );
},
console_shutdown => sub { delete $_[HEAP]{console} },
_stop => sub { },
}, );
POE::Session->create( inline_states => {
_start => sub {
$poe_kernel->alias_set('child_handler');
$poe_kernel->post(console_handler => 'console_output',
"Starting child...");
$_[HEAP]{child} = POE::Wheel::Run->new( Program => \@CHILD,
StdoutEvent => "child_stdout", StderrEvent => "child_stderr", );
$poe_kernel->sig_child($_[HEAP]{child}->PID, "child_signal");
},
child_stdin => sub {
my ($stdin) = $_[ARG0];
warn localtime." Send STDIN <$stdin>\n";
$_[HEAP]{child}->put($stdin);
},
child_stdout => sub {
my ($stdout) = $_[ARG0];
warn localtime." Got STDOUT <$stdout>\n";
$poe_kernel->post(console_handler => 'console_output',
"Child said <$stdout>");
},
child_stderr => sub {
my ($stderr) = $_[ARG0];
warn localtime." Got STDERR <$stderr>\n";
$poe_kernel->post(console_handler => 'console_output',
"Child STDERR <$stderr>");
},
child_signal => sub {
my ($status) = $_[ARG2];
$poe_kernel->post(console_handler => 'console_output',
"Child process exited with status $status.");
$poe_kernel->delay('child_kill');
delete $_[HEAP]{child};
},
child_shutdown => sub {
$poe_kernel->post(console_handler => 'console_output',
"Sending child process SIGINT...");
$_[HEAP]{child}->kill('INT');
$poe_kernel->delay('child_kill', 5);
},
child_kill => sub {
return unless defined $_[HEAP]{child};
$poe_kernel->post(console_handler => 'console_output',
"Sending child process SIGKILL.");
$_[HEAP]{child}->kill('KILL');
delete $_[HEAP]{child};
},
_stop => sub { },
}, );
POE::Session->create( inline_states => {
_start => sub {
$poe_kernel->alias_set('signal_handler');
$poe_kernel->sig(INT => 'signal_shutdown');
$poe_kernel->sig(TERM => 'signal_shutdown');
$poe_kernel->sig(HUP => 'signal_shutdown');
},
signal_shutdown => sub {
my ($signal) = $_[ARG0];
warn $signal ? "Got $signal, " : '', "Shutting down\n";
$poe_kernel->post(child_handler => 'child_shutdown');
$poe_kernel->post(console_handler => 'console_shutdown');
$poe_kernel->sig_handled;
},
_stop => sub { },
}, );
$poe_kernel->run;
示例会话:
Starting child...
send Foo
Sat Jun 2 16:44:37 2018 Send STDIN <Foo>
Sat Jun 2 16:44:37 2018 Got STDOUT <FOO>
Child said <FOO>
send Bar
Sat Jun 2 16:44:39 2018 Send STDIN <Bar>
Sat Jun 2 16:44:42 2018 Got STDOUT <foo>
Sat Jun 2 16:44:42 2018 Got STDOUT <BAR>
Child said <foo>
Child said <BAR>
Sat Jun 2 16:44:47 2018 Got STDOUT <bar>
Child said <bar>
quit
Got user request, Shutting down
Sending child process SIGINT...
Child process exited with status 2.
正如您所看到的,控制台在子进程运行时保持交互状态,子进程的输出异步显示(send Foo
,send Bar
和quit
是我的控制台输入)。请注意,如果您需要输入历史记录等高级功能,也可以使用POE::Wheel::ReadLine
代替POE::Wheel::ReadWrite
。