我的任务是从perl启动一个程序,并将控制流保持在启动任务中,并在标量变量中捕获程序的输出。该脚本应该只使用perl基础包中提供的perl模块。
我的第一个方法是
use POSIX;
use IPC::Open3;
use strict;
my ($invar, $outvar, $errvar, $in, $out, $err, $pid, $pidr);
open($in, "<",\$invar);
open($out, ">",\$outvar);
open($err, ">",\$errvar);
my $cmd = "sleep 5; echo Test; sleep 5; echo Test; sleep 5;";
$pid = open3($in, $out, $err, $cmd);
my $num = 0;
for($pidr = $pid; $pidr >= 0;)
{
sleep(1) if ($pidr = waitpid($pid, WNOHANG)) >= 0;
print "$num: $outvar" if $outvar;
++$num;
}
close($in);
close($out);
close($err);
开始时没有任何反应。已启动程序的输出不会进入$outvar
。为了测试我的基本想法是否失败,我尝试了这个:
my $outvar = "";
my $out;
open($out, ">", \$outvar);
print $out "Test\n";
print "--1--\n$outvar";
print $out "Test2\n";
print "--2--\n$outvar";
按预期正确输出:
--1--
Test
--2--
Test
Test2
问题是:为什么上述程序不能用作测试示例并输出应该在$outvar
中的文本?
工作解决方案要复杂得多(当您添加本示例中遗漏的所有安全检查时):
use POSIX;
use IPC::Open3;
use strict;
my ($invar, $outvar, $errvar, $in, $out, $err, $pid, $pidr);
open($in, "<",\$invar);
open($out, ">",\$outvar);
open($err, ">",\$errvar);
my $cmd = "sleep 5; echo Test; sleep 5; echo Test; sleep 5;";
$pid = open3($in, $out, $err, $cmd);
my $num = 0;
for($pidr = $pid; $pidr >= 0;)
{
sleep(1) if ($pidr = waitpid($pid, WNOHANG)) >= 0;
my $obits; vec($obits, fileno($out), 1) = 1;
if(select($obits, undef, undef, 0) > 0)
{
my $buffer;
sysread($out, $buffer, 10240);
print "$num: $buffer" if $buffer;
}
++$num;
}
close($in);
close($out);
close($err);
它正确打印(应该以类似的方式执行第一个):
5: Test
10: Test
对于示例,我删除了大部分错误处理和STDERR的类似代码。
答案 0 :(得分:1)
open($out, ">", \$outvar);
不会创建系统文件句柄。您会注意到fileno($out)
是-1
。一个进程不能写入另一个进程的内存,更不用说操纵它的变量,因此整个概念存在缺陷。使用IPC::Run3或IPC::Run;他们有效地为您实现select
循环。对于大多数应用来说,open3
太低级了。
第二个代码段有效,因为open3
的行为就像$in
,$out
和$err
是未打开的句柄一样。你可能已经完成了
$in = gensym();
$out = gensym();
$err = gensym();
但同样,你应该使用IPC :: Run3或IPC :: Run。您的手动版本会遇到一些错误,包括可能导致死锁的错误。