Perl IPC ::运行附加输出并解析stderr,同时将其保存在一批文件中

时间:2011-06-21 14:23:26

标签: perl file-io ipc

我正试图绕过IPC :: Run以便能够执行以下操作。有关文件列表:

my @list = ('/my/file1.gz','/my/file2.gz','/my/file3.gz');

我想执行一个内置解压缩的程序,对它们进行一些编辑和过滤,并打印到stdout,给stderr一些统计信息:

~/myprogram options $file

我想将列表中所有文件的执行stdout附加到一个$ out文件中,并且能够将每个stderr中的几行作为变量进行解析和存储,同时将其余部分写出来分别为每个输入文件的fileN.log文件。 我希望stdout全部进入“>> $ all_into_one_single_out_file”,这是我想要保存在不同日志中的错误。

阅读完手册之后,我已经走到了下面的代码,其中评论的部分我不知道该怎么做:

for $file in @list {
  my @cmd;
  push @cmd, "~/myprogram options $file";
  IPC::Run::run \@cmd, \undef, ">>$out", 
    sub { 
      my $foo .= $_[0]; 
      #check if I want to keep my line, save value to $mylog1 or $mylog2
      #let $foo and all the other lines be written into $file.log
    };
}

有什么想法吗?

1 个答案:

答案 0 :(得分:3)

首先要做的事情。 my $foo .= $_[0]没有必要。 $foo是一个新的(空)值,因此通过.=附加到它不会做任何事情。你真正想要的是一个简单的my ($foo) = @_;

接下来,您希望输出转到每个命令的一个特定文件,同时(取决于某些条件)将相同的输出放到公共文件中。

Perl(以及其他语言)有很好的工具来帮助解决这类问题,它被称为闭包。在子程序定义时,无论哪个变量都在范围内,这些变量都可供您使用。

use strict;
use warnings;

use IPC::Run qw(run new_chunker);

my @list           = qw( /my/file1 /my/file2 /my/file3 );

open my $shared_fh, '>', '/my/all-stdout-goes-here' or die;
open my $log1_fh, '>', '/my/log1' or die "Cannot open /my/log1: $!\n";
open my $log2_fh, '>', '/my/log2' or die "Cannot open /my/log2: $!\n"; 

foreach my $file ( @list ) {
  my @cmd = ( "~/myprogram", option1, option2, ..., $file );

  open my $log_fh, '>', "$file.log"
      or die "Cannot open $file.log: $!\n";

  run \@cmd, '>', $shared_fh,
             '2>', new_chunker, sub {
      # $out contains each line of stderr from the command
      my ($out) = @_;
      if ( $out =~ /something interesting/ ) {
          print $log1_fh $out;
      }
      if ( $out =~ /something else interesting/ ) {
          print $log2_fh $out;
      }
      print $log_fh $out;
      return 1;
    };
}

当每个输出文件句柄不再被任何内容引用时,它们将被关闭 - 在本例中是在此片段的末尾。

我修复了您的@cmd,但我不知道您的option1option2,...将是什么。

我也改变了你打电话run的方式。您可以使用简单的>来调用它来告诉它下一步是输出,而new_chunker(来自IPC :: Run)会将输出分成一行一行而不是一次性获得所有输出。

我还跳过了您输出到.gz个文件的事实。如果要写入压缩文件,而不是打开:

open my $fh, '>', $file  or die "Cannot open $file: $!\n";

刚开放:

open my $fh, '|-', "gzip -c > $file"  or die "Cannot startup gzip: $!\n";

这里要小心,因为这是一个命令注入的好地方(例如让$file成为/dev/null; /sbin/reboot。如何处理这个问题在很多很多其他地方都给出了,超出了你的范围实际上是在问。

EDIT:重新阅读问题,并更改答案以更准确地反映实际问题。

EDIT2::根据您的评论更新。所有stdout都转到一个文件,stderr from命令被送到内联子程序。还修复了一个愚蠢的错字(语法是伪代码而不是Perl)。