【问题标题】:How to redirect Parallel::ForkManager and Exec's STDOUT to file如何将 Parallel::ForkManager 和 Exec 的 STDOUT 重定向到文件
【发布时间】:2017-08-05 21:49:10
【问题描述】:

我正在尝试使用 Perl Parallel:ForkManager 运行多个命令。我希望这些命令在不同的时间段运行。我正在使用一个子例程 is_time_elapsed 来检查是否该运行该命令。如果子例程返回 0,那么我正在使用并行 fork 管理器运行命令。命令正在执行,但我在屏幕上看到 STDOUT。相反,我想将 STDOUT 和 STDERR 重定向到文件中。如何防止 STDOUT 在屏幕中看到并重定向到文件

我的要求是在时间过去后在后台运行命令。我需要记录命令完成执行时的执行时间。这个脚本将是一个主脚本,它将无限运行并在时间过去时运行命令。除了 Parallel::ForkManager 之外,是否还有其他模块可以实现此目的。

my $forkMgr = Parallel::ForkManager->new(15);
$forkMgr->run_on_finish(
    sub {
        ($pid, $exitCode, $ident) = @_;
        print "Ended ==> $ident\n";
    }
);

while (1) {
    foreach my $cmd (@commands) {
        if (is_time_elapsed($cmd)) {
            $forkMgr->start($cmd)
               and next;
            exec("$cmd")
               or die("exec: $!");
            $forkMgr->finish;
        }
    }
    sleep 30
}

【问题讨论】:

  • 听起来你是在滚动自己的crond。无论如何,您是否需要程序中的STDOUT?您可以永久覆盖它,也可以捕获它。
  • 不完全一样,但类似于 cron :)。没有 STDOUT 我没问题,只需要 STDERR 以防执行的命令出现任何问题。

标签: perl parallel-processing fork


【解决方案1】:

在 Perl 中,有几种方法可以实现这一点。您还没有说要发送到文件的确切输出,或者您是否需要真正的STDOUT。让我们探索您的选择。

您在程序中的任何地方都不需要STDOUT

如果您的主程序以及所有工作程序都不应该向STDOUT 打印任何内容,您可以通过输出重定向运行它。这使您能够在开发期间对 STDOUT 进行调试,并在生产中写入文件。

只需在命令中附加>output.log 即可启动您的程序。所有分叉的输出也将转到该文件。 STDERR 将保持不变。

$ perl forker.pl >output.log

如果您希望所有输出都转到文件,这应该是首选方式。

或者,您可以在 Perl 中实现它。这将使您失去在控制台上调试的能力,并且您将始终必须查看文件。使用select 更改print 的默认句柄。确保为新句柄使用追加模式>>

open my $fh, '>>', 'output.log' or die $!;
select $fh;

my $forkMgr = Parallel::FormManager->new(15);
# ...

现在所有输出也将转到文件中。 See this blog post 了解有关其工作原理以及如何恢复原始 STDOUT 的更多信息。

捕捉STDOUT

Capture::Tiny 非常适合抓住 STDOUT 的东西。你可以用它来包装exec

use Capture::Tiny 'capture_stdout';

# ...

my $stdout = capture_stdout {
    exec($cmd);
}

现在您可以将其写入文件。

使用记录器

Log::Log4Perl 是高级日志记录的首选模块。您应该能够告诉它不仅从STDERR 获取所有内容,还从STDOUT 获取所有内容。我没有研究过这个,但它应该可以工作。

【讨论】:

  • select 方法行不通。子进程不知道其 Perl 父进程的内部结构。
  • 没有解决方案重定向 STDERR!
  • @ikegami 这就是重点。 OP在评论中说。他们想将 STDOUT 写入文件,并保留 STDERR。
  • 他们说主程序需要 STDERR 未重定向(在该评论中),但他们也说子程序需要 STDERR 重定向(在问题中)。
【解决方案2】:

重定向整个程序及其子程序的所有输出:

main_program >log 2>&1

只重定向孩子的输出:

if (is_time_elapsed($cmd)) {
    $forkMgr->start($cmd)
       and next;
    open(STDOUT, '>>', 'log')
       or die("open: $!");
    open(STDERR, '>>&', \*STDOUT)
       or die("open: $!");
    exec("$cmd")
       or die("exec: $!");
}

如果您愿意,您甚至可以将每个孩子的输出重定向到不同的文件。


注意:我去掉了$forkMgr->finish。它从未达到(这很好)。

【讨论】:

    猜你喜欢
    • 2014-07-22
    • 2011-11-23
    • 2010-10-30
    • 2021-09-08
    • 2011-03-02
    • 1970-01-01
    • 1970-01-01
    • 2016-02-19
    • 1970-01-01
    相关资源
    最近更新 更多