从命令中捕获STDERR并在窗口下的perl中管道STDOUT

时间:2013-06-21 15:30:17

标签: windows perl pipe stderr

我正在使用一个Perl脚本,该脚本使用系统来运行这样的外部命令管道:

system( "command1 | command2 | command3 > outfile" );

现在我想将所有这些命令中的STDERR捕获到一个文件中。这适用于OS X:

system( "command1 2> error.log | command2 2> error.log | command3 2> error.log > outfile" );

但不在Windows中,我收到错误:

  

“进程无法访问该文件,因为它正由另一个进程使用”

有没有解决方法?我需要它是便携式的,所以如果可能的话,我想避免使用模块。 提前谢谢。

2 个答案:

答案 0 :(得分:2)

没有shell,没有临时文件,也没有来自核心之外的东西。

use strict;
use warnings;

use IPC::Open3 qw( open3 );

my @pids;
{
   my @cmd1 = ( 'perl', '-E',  q{say for qw( ABC DEF );}     );
   my @cmd2 = ( 'perl', '-pe', q{$_=lc; warn(qq{x\n});}      );
   my @cmd3 = ( 'perl', '-pe', q{$_=ucfirst; warn(qq{y\n});} );

   my $nul = $^O eq 'MSWin32' ? 'nul' : '/dev/null';

   open(local *CHILD_STDIN,  '<', $nul       ) or die $!;
   open(local *CHILD_STDOUT, '>', 'outfile'  ) or die $!;
   open(local *CHILD_STDERR, '>', 'error.log') or die $!;

   push @pids, open3('<&CHILD_STDIN', local *PIPE1,     '>&CHILD_STDERR', @cmd1);
   push @pids, open3('<&PIPE1',       local *PIPE2,     '>&CHILD_STDERR', @cmd2);
   push @pids, open3('<&PIPE2',       '>&CHILD_STDOUT', '>&CHILD_STDERR', @cmd3);

   *CHILD_STDIN if 0;  # Silence warning. Already closed by open3.
   *PIPE1       if 0;  # Silence warning. Already closed by open3.
   *PIPE2       if 0;  # Silence warning. Already closed by open3.

   close(CHILD_STDOUT);
   close(CHILD_STDERR);
}

waitpid($_, 0) for @pids;

答案 1 :(得分:0)

那是因为'&gt;'不喜欢分享文件。 给管道的每个阶段提供自己的错误日志,然后在管道完成后执行类似的操作:

  system("cat error1.log erorr2.log error3.log > error.log");

这是一种独立于平台的聚合日志方式:

my @error_logs = qw( error1.log error2.log error3.log );
open my $errlog, ">>", "error.log" || die "probelm opening error log: $!";

foreach my $sublog ( @error_logs ) {
   open my $fh, "<", $sublog || die "oh boy: $sublog: $!";
   print "$sublog:"
   print $errlog while $fh;
   close $fh;
}

close $errlog;

但如果您决定以这种方式倾斜,那么IO::CatFile::Cat也存在。


1)更正了不会共享文件的自私的名称。

2)添加了日志文件集合