2013-08-22 41 views
2

我使用以下脚本从作为参数传递的命令中捕获STDINSTDOUTSTDERR如何检查IPC :: open3执行的命令是否被挂起?

#!/usr/bin/perl 

use strict; 
use warnings; 

use IPC::Open3; 

local(*CMD_IN, *CMD_OUT, *CMD_ERR); 

my $pid = open3(*CMD_IN, *CMD_OUT, *CMD_ERR, $ARGV[0]); 

close(CMD_IN); 

my @stdout_output = <CMD_OUT>; 
my @stderr_output = <CMD_ERR>; 

close(CMD_OUT); 
close(CMD_ERR); 

waitpid ($pid, 0); # reap the exit code 

print "OUT:\n", @stdout_output; 
print "ERR:\n", @stderr_output; 

这一切都很好,除了我不确定如何监视命令传递是否挂起。你能提出一个建议吗?

我借用了这个片段,最初来自'编程Perl'。

回答

5

您可以使用selectIO::Select并提供超时。如果你想从stdout和stderr中读取,你应该这样做(参见文档IPC::Open3)。

下面是一个使用IO::Select一个示例程序:

#!/usr/bin/perl 

use strict; 
use warnings; 

use IO::Select; 
use IPC::Open3; 
use Symbol 'gensym'; 

my ($cmd_in, $cmd_out, $cmd_err); 
$cmd_err = gensym; 
my $pid = open3($cmd_in, $cmd_out, $cmd_err, $ARGV[0]); 

close($cmd_in); 

my $select = IO::Select->new($cmd_out, $cmd_err); 
my $stdout_output = ''; 
my $stderr_output = ''; 

while (my @ready = $select->can_read(5)) { 
    foreach my $handle (@ready) { 
     if (sysread($handle, my $buf, 4096)) { 
      if ($handle == $cmd_out) { 
       $stdout_output .= $buf; 
      } 
      else { 
       $stderr_output .= $buf; 
      } 
     } 
     else { 
      # EOF or error 
      $select->remove($handle); 
     } 
    } 
} 

if ($select->count) { 
    print "Timed out\n"; 
    kill('TERM', $pid); 
} 

close($cmd_out); 
close($cmd_err); 

waitpid($pid, 0); # reap the exit code 

print "OUT:\n", $stdout_output; 
print "ERR:\n", $stderr_output; 

注:

  • 我使用的词汇瓦尔文件句柄。这需要使用gensym作为stderr句柄。
  • can_read的参数是以秒为单位的超时。
  • 我使用sysread作为非缓冲IO。
  • 如果发生读取超时,我终止孩子。
+0

非常感谢!我实际上实现了一些可以完成这项工作的东西,但我宁愿避免使用信号并改用您的解决方案。无论如何,如果其他人感兴趣,我会发布它。 – Nobilis

1

我根据this的答案提出了以下解决方案。

但是使用select并避免信号,因为在nwellnhof的例子看起来更清洁,这就是为什么我接受它。如果有人感兴趣,我在此张贴:

my $pid = open3(*CMD_IN, *CMD_OUT, *CMD_ERR, $cmd); 

if ($pid > 0){ 
    eval{ 
     local $SIG{ALRM} = sub {kill 9, $pid;}; 
     alarm 6; 
     waitpid($pid, 0); 
     alarm 0; 
    }; 
}