Perl:在IPC::Run中检索进程的输出

Perl:在IPC::Run中检索进程的输出,perl,error-handling,Perl,Error Handling,我已经在模块中运行了一些命令,一切都很好,只是我无法访问输出(STDOUT、STDERR),进程生成并被重定向到变量中。有没有办法在错误处理中检索这些错误 @commands = (); foreach my $id (1..3) { push @commands, ["perl", "script" . $id . ".pl"]; } foreach my $cmd (@commands) { my $out = ""; my $err = ""; my $h = harne

我已经在模块中运行了一些命令,一切都很好,只是我无法访问输出(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";
}
for(我的$i=0;$i<10;$i++){
睡眠1;
打印“Hello from script 1”.localtime()。“\n”;
}

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

正如@ysth所指出的,您没有得到任何输出的原因是,与命令
$cmd
对应的进程的
STDOUT
STDERR
没有行缓冲,而是块缓冲。因此,所有输出都收集在一个缓冲区中,该缓冲区在缓冲区已满或显式刷新之前不会显示(打印)。但是,当您的命令超时时,所有输出仍在缓冲区中,尚未刷新,因此收集到父进程(脚本)中的变量
$out

还请注意,由于您的
$cmd
脚本是一个Perl脚本,因此此行为记录在:

$|

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

文档页面中还记录了以下问题(程序未连接到终端或tty):

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

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

然后,针对特定情况的一种解决方案是在脚本开头显式地将
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";
}

你是说
$out
$err
?如果
$cmd
在超时之前生成任何输出,则这些选项应可用。你确定命令超时了吗?注意:除非有超时,否则脚本不会打印任何内容。是的,我指的是那些。嗯,我没有把全部代码放在那里,这只是主要部分。我正在运行3个脚本,打印某物并进入睡眠状态,我将其中一个脚本设置为比计时器运行的时间更长。前2个打印得很好,但第3个被计时器捕捉并退出。但是输出不可用。。。我在使用Fork管理器,所以我没有写全部内容,因为它可能会变得混乱。您能否提供第三个脚本的示例,以便我们可以尝试复制?如果您没有看到第三个脚本的任何输出,它可能正在被缓冲。您是否在未超时时获得输出?@heatheice感谢您的更新。尝试将
STDOUT->autoflush(1)
放在脚本开头。根据“如果输出到终端,则标准输出通常是行缓冲的,否则块缓冲的”,使用您的解决方案确实解决了我的问题。但是我仍然会有一个问题,因为我将来不会只使用perl脚本。是否有其他模块将输出直接保存到处理程序或类似程序?我尝试过使用IO::Handler,但我没有让它工作……也许你也可以尝试愚弄正在运行的脚本,让它们相信它是通过使用Nit中描述的伪终端连接到一个真正的tty:
print“…”
技术上打印到
select()
返回的句柄,所以
select()->autoflush(1)
STDOUT->autoflush(1)更有意义。当然,
select()->autoflush(1)
也可以写成
$|=1
@ikegami谢谢你的评论!知道这一点很好,但从OP的脚本中应该(?)非常清楚,它正试图输出到STDOUT(因此所选句柄对于它们的情况确实是STDOUT),因此我不会像您建议的那样用更“正确”的方法更新我的答案。我认为它包含了足够的细节,我想避免进一步的混乱/分心,但如果可以的话,你可以保留评论。就像我说的,这只是一个谎言。
my $h = harness $cmd, \undef, '>pty>', \$out,
  timeout(12, exception => {name => 'timeout'});
eval {
    run $h;
};