Perl:如果它死了,从IPC :: Run中的进程检索输出

时间:2018-05-17 13:19:56

标签: perl error-handling

我一直使用IPC::Run模块运行一些命令,一切都很好,除了我无法访问输出(STDOUT,STDERR),生成的过程被重定向到变量。有没有办法在错误处理中检索那些?

@commands = ();
foreach my $id (1..3) {
  push @commands, ["perl", "script" . $id . ".pl"];
}

foreach my $cmd (@commands) {
  my $out = "";
  my $err = "";
  my $h = harness $cmd, \undef, \$out, \$err, timeout(12,exception => {name => 'timeout'});
  eval {
    run $h;
  };

  if ($@) {
    my $err_msg = $@; # save in case another error happens
    print "$out\n";
    print "$err\n";
    $h->kill_kill;
  }
}

我现在不需要任何输入,我只需要执行它并获得输出。

修改 我一直在使用运行perl脚本测试它,如下所示:

for (my $i = 0; $i < 10; $i++) {
  sleep 1;
  print "Hello from script 1 " . localtime() . "\n";
}

我有3个这样的脚本有不同的时间,第3个需要20秒才能完成,这比计时器中的12个要多。

1 个答案:

答案 0 :(得分:1)

如@ysth所述,您没有获得任何输出的原因是,与命令STDOUT对应的进程的STDERR$cmd不是行缓冲的,而是阻止缓冲。因此,所有输出都收集在缓冲区中,该缓冲区未显示(打印),直到缓冲区已满或明确刷新为止。但是,当您的命令超时时,所有输出仍然在缓冲区中并且尚未刷新,因此被收集到父进程(脚本)中的变量$out中。

另请注意,由于您的$cmd脚本是Perl脚本,因此perlvar中记录了此行为:

  

$ |

     

如果设置为非零,则在每次写入后立即强制刷新   或在当前选定的输出通道上打印。默认值为0   (无论通道是否真的由系统缓冲或   不; $ |只告诉您是否已明确要求Perl刷新   每次写完后)。 如果输出为STDOUT,通常会进行行缓冲   到终端和阻止缓冲否则

问题(程序未连接到终端或tty)也在IPC::Run的文档页面中注明:

  

交互式应用程序通常针对人类使用进行优化。这个可以   帮助或阻碍尝试通过类似的模块与他们互动   IPC ::运行。程序通常会在检测到时改变其行为   假设,stdin,stdout或stderr没有连接到tty   它们以批处理模式运行。这是帮助还是伤害   取决于哪些优化会发生变化。而且往往没办法   告诉我们程序在这些方面做了什么,而不是试错   偶尔,阅读来源。这包括不同的版本   和相同程序的实现。

该文档还列出了一组可能的解决方法,包括使用pseudo terminals

然后,针对您的特定情况的一个解决方案是在脚本开头明确地使STDOUT行缓冲:

STDOUT->autoflush(1);  # Make STDOUT line buffered
# Alternatively use: $| = 1; 
for (my $i = 0; $i < 10; $i++) {
  sleep 1;
  print "Hello from script 1 " . localtime() . "\n";
}

修改

如果由于某种原因无法修改正在运行的脚本,可以尝试将脚本连接到伪终端。因此,不要在脚本的源代码中插入像STDOUT->autoflush(1)这样的语句,而是可以欺骗脚本以确保它连接到终端,因此它应该使用行缓冲。对于您的情况,我们只需在调用>pty>的{​​{1}}参数之前添加\$out参数:

harness