【问题标题】:capturing STDERR from commands and pipe STDOUT in perl under windows在 windows 下的 perl 中从命令中捕获 STDERR 和管道 STDOUT
【发布时间】:2023-04-05 00:12:01
【问题描述】:

我正在编写一个 Perl 脚本,它使用 system 来运行这样的外部命令管道:

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

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

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

但不是在 Windows 中,我得到错误:

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

有什么解决方法吗?我需要它是可移植的,所以如果可能的话,我想避免使用模块。 提前致谢。

【问题讨论】:

  • modules != 不可移植,一般来说
  • 这很愚蠢。使用模块正是您需要的可移植性。
  • 对不起,我的意思是它会被其他人使用,可能根本不熟悉Perl,所以我不想让他们下载除了main之外的任何东西分配。但是我可以使用 Perl 附带的任何东西(对不起,如果我不清楚)
  • 除了安装脚本之外,您不应该要求他们做任何事情。只需将模块标记为脚本的依赖项。实际上,您可以使用核心模块 IPC::Open3 来完成。

标签: windows perl pipe stderr


【解决方案1】:

没有外壳,没有临时文件,也没有核心之外的任何东西。

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;

【讨论】:

  • 谢谢。我需要先学习一下,然后才能实现它,但它看起来需要什么。
【解决方案2】:

那是因为 '>' 不喜欢共享文件。 给流水线的每个阶段自己的错误日志,然后在流水线完成后执行类似这样的操作:

  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) 添加日志文件收集

【讨论】:

  • 经验证据表明并非如此。
  • 这只是迂腐。
  • 谢谢,我想这是唯一的选择。然后“猫”是平台相关的,所以我会避免它。但我想我可以忍受几个日志。
  • 非常感谢。我会看看你建议的其他模块。
猜你喜欢
  • 2021-09-05
  • 2019-10-26
  • 2020-06-23
  • 2012-02-02
  • 2012-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多