Мне нужно, чтобы это внешнее приложение продолжало работать, а скрипт perl продолжал отвечать до тех пор, пока я не уничтожу оба.
Это интересный вопрос - один из возможных способов решить это с помощью событиярамочная петля типа POE
.Несмотря на то, что у него есть некоторая кривая обучения (но см. поваренная книга ), я считаю, что он отлично подходит для таких вещей, как специализированные сетевые серверы, или для таких вещей, как ваш случай - оставаться интерактивным на консоли, в то время как другие вещи(сетевые соединения, последовательные порты и т. д.).
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
, если вам нужны расширенные функции, такие как история ввода.