Perl:将STDERR重定向到文件而不创建空文件?

| 我使用以下命令在Perl脚本中重定向STDOUT和STDERR:
open STDOUT, \'>\', $logfile or die \"Can\'t redirect STDOUT: $!\";
open STDERR, \">&STDOUT\" or die \"Can\'t dup for STDERR: $!\";
保存和还原文件句柄之前和之后... 问题是,如果程序没有输出,我最终会得到一个大小为0的文件,但我根本不想有任何文件。我该怎么做而又不求助于手动检查和删除文件? 谢谢!
已邀请:
您可以将STDOUT绑定到一个类,该类将延迟打开目标文件,直到第一次将句柄写入:
package FastidiousHandle;

use Tie::StdHandle;
use strict;

our @ISA = \'Tie::StdHandle\';

sub TIEHANDLE {
    my ($class, @args) = @_;
    my $self = $class->SUPER::TIEHANDLE;
    ${*$self}{openargs} = \\@args;
    return $self;
}

sub WRITE {
    my $self = shift;
    my $openargs = delete ${*$self}{openargs};
    $self->OPEN(@$openargs) if $openargs;
    $self->SUPER::WRITE(@_);
}

1;
然后在您的主程序中,您会说:
tie *STDOUT, \'FastidiousHandle\', \'>\', $path;
my $saved_stderr = *STDERR;
*STDERR = *STDOUT;
要恢复以前的句柄,您会说:
*STDERR = $saved_stderr;
untie *STDOUT;
只需检查最后是否已写入任何内容,否则请删除该文件。确保已打开自动冲洗功能。
use IO::Handle;
...
open STDOUT, \'>\', $logfile or die \"Can\'t redirect STDOUT: $!\";
open STDERR, \">&STDOUT\" or die \"Can\'t dup for STDERR: $!\";

STDOUT->autoflush(1);
STDERR->autoflush(1);

...

END {
    unlink $logfile if -z $logfile;
}
还是旧式的...
open STDOUT, \'>\', $logfile or die \"Can\'t redirect STDOUT: $!\";
open STDERR, \">&STDOUT\" or die \"Can\'t dup for STDERR: $!\";
select(STDERR); $|=1; select(STDOUT); $|=1;

END {
    unlink $logfile if -z $logfile;
}
我能想到的唯一方法是派生一个子进程,该子进程通过管道将所有内容发送回去(想想IO :: Pipe或类似IPC :: Open2的方式-无论哪种方式,您仍将STDERR重定向到子代中的STDOUT),然后在父项中,将管道中获得的内容写入日志文件-这使您在第一次拥有数据时可以打开日志文件。例如:
#!/usr/bin/perl

use Proc::Fork;
use IO::Pipe;

sub pipe_to_logfile
{
    my $log = shift;
    my @cmd = @_;

    my $pipe = IO::Pipe->new();

    run_fork {
        child {
            $pipe->writer();
            open STDOUT, \'>&\', $pipe or die \"Can\'t redirect STDOUT: $!\";
            open STDERR, \'>&STDOUT\'  or die \"Can\'t redirect STDERR: $!\";

            exec(@cmd);
        }
        parent {
            $pipe->reader();
            my $fh;

            while(<$pipe>)
            {
                unless ($fh)
                {
                    open $fh, \'>\', $log or die \"Can\'t write to $log: $!\";
                }
                print $fh $_;
            }
        }
    }
}

pipe_to_logfile(\'/tmp/true.out\', \'true\');
pipe_to_logfile(\'/tmp/ls.out\', qw(ls /));
运行此命令时,我得到:
$ ls /tmp/*.out
ls: cannot access /tmp/*.out: No such file or directory
$ cd tmp
$ perl foo.pl
$ ls /tmp/*.out
/tmp/ls.out
希望能有所帮助。
您不希望延迟打开文件,如果确实延迟打开任何诸如权限错误之类的问题,或者文件路径中缺少目录将导致程序在第一个打印语句中失败。假设您听起来似乎有程序运行无法打印任何内容,那么您将来可能会在某个随机时间遇到程序故障,因为它只是打印到一个无法打开数月的文件中。到那时,您或您的继任者可能已经忘记了此功能。 最好在完成后检查文件,以查看文件是否为空并将其删除。 如果您想为自己做,可以将逻辑包装在一个类中。
package My::File;
use strict;
use warnings;
use base qw(IO::File);

sub new {
    my ($class, $file, @args) = @_;
    my $self = $class->SUPER::new($file, @args);
    if ($self) {
        *{$self}->{file} = $file;
    }
    return $self;
}

sub DESTROY {
    local $@;
    my ($self) = @_;
    $self->flush;
    if (-e *{$self}->{file} && -z *{$self}->{file}) {
        unlink *{$self}->{file};
    }
    return;
}

package main;

my $fh1 = My::File->new(\"file_1\", \"w\");
my $fh2 = My::File->new(\"file_2\", \"w\");

print $fh1 \"This file should stay\\n\";
该代码不是真正的生产就绪版本,它不会尝试处理可以调用​​
IO::File->new()
的所有方式,并且还应该以类似于
new
的方式覆盖对
$file_obj->open()
的调用。它还可以更好地处理错误。

要回复问题请先登录注册