我想执行外部命令rtmpdump
并分别读取STDOUT
和STDERR
,但不要等到这样的命令结束,而是以批量读取其部分输出,如果可用的话...
在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的二进制输出应存储在变量中,而不是打印。
答案 0 :(得分:1)
如果您使用的是POSIX系统,请尝试使用Expect.pm。这正是它旨在解决的问题,它还简化了向生成的进程发送击键的任务。
答案 1 :(得分:1)
在readline
循环中使用阻止功能(例如<>
又名read
,select
等),无法使用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 ...