【问题标题】:How to check if command executed with IPC::open3 is hung?如何检查使用 IPC::open3 执行的命令是否挂起?
【发布时间】:2013-08-22 06:49:18
【问题描述】:

我正在使用以下脚本从作为参数传递的命令中捕获STDINSTDOUTSTDERR

#!/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;

除了我不确定如何监控传递的命令是否挂起之外,一切都很好。你能推荐一个方法吗?

我最初从“Programming Perl”借用了这个 sn-p。

【问题讨论】:

    标签: perl ipc


    【解决方案1】:

    您可以使用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。
    • 如果出现读取超时,我会终止子进程。

    【讨论】:

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

    我根据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;
        };
    }
    

    【讨论】:

      猜你喜欢
      • 2019-03-11
      • 1970-01-01
      • 2018-01-14
      • 2013-12-22
      • 1970-01-01
      • 2023-03-07
      • 2021-07-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多