【问题标题】:How do I run perl scripts in parallel and capture outputs in files?如何并行运行 perl 脚本并在文件中捕获输出?
【发布时间】:2021-11-16 01:43:42
【问题描述】:

我需要并行运行我的 perl 测试并将 STDOUT 和 STDERR 捕获到每个测试文件的单独文件中。即使在一个文件中捕获,我也没有成功。我一直在如此并且没有运气。这是我开始的地方(我会省去你所有的变化)。任何帮助是极大的赞赏。谢谢!

foreach my $file ( @files) {
    next unless $file =~ /\.t$/;
    print "\$file = $file\n";

    $file =~ /^(\w+)\.\w+/;
    
    my $file_pfx = $1;
    my $this_test_file_name = $file_pfx . '.txt';
    
    system("perl $test_dir\\$file > results\\$test_file_name.txt &") && die "cmd failed: $!\n";

}

【问题讨论】:

  • GNU Parallel 将是一个简单的选择 - 实际上是 Perl 脚本本身 :-)
  • Parallel::ForkManager 模块易于使用。至于保存每个进程的输出,在每个子进程内部将其STDOUT 和STDERR 流重定向到文件(或变量)。
  • 你试过像prove -j4 -a report.tgz这样的prove命令吗? prove 可以开箱即用地并行运行测试。 perldoc.perl.org/prove

标签: perl redirect parallel-processing background stdout


【解决方案1】:

这是一个使用Parallel::ForkManager 生成单独进程的简单示例。

在每个进程中,STDOUT 和 STDERR 流被重定向,在演示中以两种方式重定向:STDOUT 到一个变量,然后可以根据需要传递(这里转储到一个文件中),以及 @ 987654331@ 直接写入文件。或者使用一个库,在单独的代码 sn-p 中有一个示例。

数字1..6 代表每个孩子将从中挑选并处理的数据批次。只有三个进程会立即启动,然后当一个进程完成时,另一个进程会在其位置启动。†(在这里它们几乎立即退出,“工作”是微不足道的。)

use warnings;
use strict;
use feature 'say';

use Carp qw(carp)        
use Path::Tiny qw(path); 
use Parallel::ForkManager; 

my $pm = Parallel::ForkManager->new(3); 

foreach my $data (1..6) { 
    $pm->start and next;     # start a child process
    proc_in_child($data);    # code that runs in the child process
    $pm->finish;             # exit it
}
$pm->wait_all_children;      # reap all child processes

say "\nParent $$ done\n";
    
sub proc_in_child {
    my ($data) = @_; 
    say "Process $$ with data $data";  # still shows on terminal

    # Will dump all that was printed to streams to these files
    my (outfile, $errfile) = 
        map { "proc_data-${data}_" . $_ . ".$$.out" } qw(stdout stderr);

    # Redirect streams
    # One way to do it, redirect to a variable (for STDOUT)...  
    open my $fh_stdout, ">", \my $so or carp "Can't open handle to variable: $!";
    my $fh_STDOUT = select $fh_stdout;
    # ...another way to do it, directly to a file (for any stream)
    # (first 'dup' it so it can be restored if needed)
    open my $SAVEERR, ">&STDERR"  or carp "Can't dup STDERR: $!";
    open *STDERR, ">", $errfile or carp "Can't redirect STDERR to $errfile: $!";

    # Prints wind up in a variable (for STDOUT) and a file (for STDERR)
    say  "STDOUT: Child process with pid $$, processing data #$data"; 
    warn "STDERR: Child process with pid $$, processing data #$data"; 

    close $fh_stdout;
    # If needed to restore (not in this example which exits right away)
    select $fh_STDOUT;
    open STDERR, '>&', $SAVEERR  or carp "Can't reopen STDERR: $!";

    # Dump all collected STDOUT to a file (or pass it around, it's a variable)
    path( $outfile )->spew($so);

    return 1
}

虽然STDOUT 被重定向到一个变量,STDERR 不能以这种方式重定向,它直接转到一个文件。见open。但是,也有一些方法可以在变量中捕获它。

然后您可以使用模块从子进程返回到父进程的能力,然后父进程可以处理这些变量。参见例如 this post 和 this post 和 this post。 (还有更多,这些只是我知道的。)或者实际上只是将它们转储到文件中,就像这里所做的那样。

另一种方法是使用可以运行代码和重定向输出的模块,例如Capture::Tiny

use Capture::Tiny qw(capture);

sub proc_in_child {
    my ($data) = @_; 
    say "Process $$ with data $data";  # on terminal

    # Run code and capture all output
    my ($stdout, $stderr, @results) = capture {
          say  "STDOUT: Child process $$, processing data #$data";
          warn "STDERR: Child process $$, processing data #$data"; 

          # return results perhaps...
          1 .. 4;
    }

    # Do as needed with variables with collected STDOUT and STDERR
    # Return to parent, or dump to file:
    my ($outfile, $errfile) = 
        map { "proc_data-${data}_" . $_ . ".$$.out" } qw(stdout stderr);

    path($outfile) -> spew( $stdout );
    path($errfile) -> spew( $stderr );

    return 1
}    

† 这样可以保持相同数量的进程运行。或者,可以将其设置为等待整个批次完成,然后再开始另一批次。一些操作细节见this post

【讨论】:

    【解决方案2】:

    我认为,最简单的方法是在“系统”命令中使用 shell 重定向。顺便说一句,用“&”从中产生不受控制的子进程让我皱眉。

    这是一个使用 shell 重定向和分叉的简单示例。

    #!/usr/bin/perl
    use strict;
    
    for my $i (0..2) {
        my $stdoutName = "stdout$i.txt";
        my $stderrName = "stderr$i.txt";
        my $pid = fork();
        if($pid == 0) {
            system("perl mytest.pl 1>$stdoutName 2>$stderrName"); #redirects are here 1> (stdout) and 2> (stderr)
            exit $?;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-01-06
      • 1970-01-01
      • 2014-05-11
      • 1970-01-01
      • 1970-01-01
      • 2011-04-18
      • 1970-01-01
      • 1970-01-01
      • 2011-02-10
      相关资源
      最近更新 更多