具有标量目标的Perl文件句柄和IPC :: Open3

时间:2016-08-16 19:07:52

标签: perl asynchronous filehandle

我的任务是从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的类似代码。

1 个答案:

答案 0 :(得分:1)

open($out, ">", \$outvar);不会创建系统文件句柄。您会注意到fileno($out)-1。一个进程不能写入另一个进程的内存,更不用说操纵它的变量,因此整个概念存在缺陷。使用IPC::Run3IPC::Run;他们有效地为您实现select循环。对于大多数应用来说,open3太低级了。

第二个代码段有效,因为open3的行为就像$in$out$err是未打开的句柄一样。你可能已经完成了

$in = gensym();
$out = gensym();
$err = gensym();

但同样,你应该使用IPC :: Run3或IPC :: Run。您的手动版本会遇到一些错误,包括可能导致死锁的错误。