【发布时间】:2020-03-11 14:42:20
【问题描述】:
在 Perl(使用版本 5.30.0)中,是否可以通过编程方式(即在我的 Perl 代码中)更改一些设置,这些设置会导致 STDOUT 和 STDERR 被重定向到我的 Perl 进程将要的任何子进程的文本文件从那一刻开始?这里的困难在于我不控制实际启动这些子进程的代码(如果我这样做了,这将是非常微不足道的)。我希望存在某种我可以在我的 Perl 进程中设置的 IO 标志,这将导致所有新启动的子进程将其 STDOUT / STDERR 通道重定向到我选择的目的地(除非 system() / exec() / 用于启动它们的任何调用都显式提供了另一个 STDOUT / STDERR 目标)。
我需要的(高度简化的)用例如下。我编写了一个通用库函数,它执行调用者指定的任务,除其他外,将用户提供的任务生成的所有 STDOUT / STDERR 输出存储在文本文件中。该任务以 Perl 函数引用的形式提供。到目前为止,我只是设法重定向了我的 Perl 进程生成的 STDOUT / STDERR 输出:
local *STDOUT;
local *STDERR;
open( STDOUT, '>', $buildLogFilePath ) or die sprintf(
"ERROR: [%s] Failed to redirect STDOUT to \"%s\"!\n",
$messageSubject,
$buildLogFilePath
);
open ( STDERR, '>&', STDOUT ) or die sprintf(
"ERROR: [%s] Failed to redirect STDERR to \"%s\"!\n",
$messageSubject,
$buildLogFilePath
);
因此,只要用户提供的函数不启动任何子进程,我的功能就可以按预期工作,但是当它启动子进程时,该进程的输出只会转到默认的 STDOUT / STDERR 通道,有效地破坏了我的功能。
编辑:我最终使用了下面 ikegami 描述的 STDOUT / STDERR 重新指向技巧。为了更容易地重用这个技巧,我将代码包装在一个小实用程序类中,该类在构造函数中重定向 STDOUT / STDERR,然后在析构函数中将它们恢复为原始值:
package TemporaryOutputRedirector;
use strict;
use warnings FATAL => 'all';
use Carp::Assert;
sub new
{
my ( $class, $newDest ) = @_;
open( my $savedStdout, '>&', STDOUT ) or die sprintf(
"ERROR: Failed to save original STDOUT in process %i!\n",
$$
);
open( STDOUT, '>', $newDest ) or die sprintf(
"ERROR: Failed to redirect STDOUT to \"%s\" in child process %i!\n",
$newDest,
$$
);
open( my $savedStderr, '>&', STDERR ) or die sprintf(
"ERROR: Failed to save original STDERR in process %i!\n",
$$
);
open ( STDERR, '>&', STDOUT ) or die sprintf(
"ERROR: Failed to redirect STDERR in child process %i!\n",
$$
);
my %memberData = (
'newDest' => $newDest,
'savedStdout' => $savedStdout,
'savedStderr' => $savedStderr
);
return bless \%memberData, ref $class || $class;
}
sub DESTROY
{
my ( $self ) = @_;
my $savedStdout = $$self{ 'savedStdout' };
assert( defined( $savedStdout ) );
open( STDOUT, '>&', $savedStdout ) or warn sprintf(
"ERROR: Failed to restore original STDOUT in process %i!\n",
$$
);
my $savedStderr = $$self{ 'savedStderr' };
assert( defined( $savedStderr ) );
open( STDERR, '>&', $savedStderr ) or warn sprintf(
"ERROR: Failed to restore original STDERR in process %i!\n",
$$
);
}
1;
这样,只需要创建一个TemporaryOutputRedirector的实例,执行应该重定向其输出的代码,然后让TemporaryOutputRedirector超出范围并让它的析构函数恢复原来的STDOUT / STDERR通道.
【问题讨论】:
-
删除
local *STDOUT; local *STDERR;