无需等待即可读取外部命令的STDOUT和STDERR

时间:2013-06-18 17:12:26

标签: perl

我想执行外部命令rtmpdump并分别读取STDOUTSTDERR,但不要等到这样的命令结束,而是以批量读取其部分输出,如果可用的话...

在Perl中执行此操作的安全方法是什么?


这是我的代码“按行”工作:

#!/usr/bin/perl

use warnings;
use strict;
use Symbol;
use IPC::Open3;
use IO::Select;

sub execute {
  my($cmd) = @_;
  print "[COMMAND]: $cmd\n";
  my $pid = open3(my $in, my $out, my $err = gensym(), $cmd);
  print "[PID]: $pid\n";
  my $sel = new IO::Select;
  $sel->add($out, $err);
  while(my @fhs = $sel->can_read) {
    foreach my $fh (@fhs) {
      my $line = <$fh>;
      unless(defined $line) {
        $sel->remove($fh);
        next;
      }
      if($fh == $out) {
        print "[OUTPUT]: $line";
      } elsif($fh == $err) {
        print "[ERROR] : $line";
      } else {
        die "[ERROR]: This should never execute!";
      }
    }
  }
  waitpid($pid, 0);
}

但我相信上面的代码只能在文本模式下工作。要使用rtmpdump作为命令,我需要以二进制模式收集部分输出,因此不要像上面代码中那样逐行读取STDOUT

STDOUT的二进制输出应存储在变量中,而不是打印。

2 个答案:

答案 0 :(得分:1)

如果您使用的是POSIX系统,请尝试使用Expect.pm。这正是它旨在解决的问题,它还简化了向生成的进程发送击键的任务。

答案 1 :(得分:1)

readline循环中使用阻止功能(例如<>又名readselect等),无法使用select

$sel->add($out, $err);

my %bufs;
while ($sel->count) {
   for my $fh ($sel->can_read) {
      my $rv = sysread($fh, $bufs{$fh}, 128*1024, length($bufs{$fh}));

      if (!defined($rv)) {
         # Error
         die $! ;
      }

      if (!$rv) {
         # Eof
         $sel->remove($fh);
         next;
      }        

      if ($fh == $err) {
         while ($bufs{$err} =~ s/^(.*\n)//) {
            print "[ERROR] $1";
         }
      }
   }
}

print "[ERROR] $bufs{$err}\n" if length($bufs{$err});

waitpid($pid, 0);

... do something with $bufs{$out} ...

但使用IPC::Run会更简单。

use IPC::Run qw( run );

my ($out_buf, $err_buf);
run [ 'sh', '-c', $cmd ],
   '>', \$out_buf, 
   '2>', sub {
      $err_buf .= $_[0];
      while ($err_buf =~ s/^(.*\n)//) {
         print "[ERROR] $1";
      }
   };

print "[ERROR] $err_buf\n" if length($err_buf);

... do something with $out_buf ...