2011-04-18 108 views
3

我重定向STDOUT和STDERR在Perl脚本有:Perl:将STDERR重定向到文件而不创建空文件?

open STDOUT, '>', $logfile or die "Can't redirect STDOUT: $!"; 
open STDERR, ">&STDOUT" or die "Can't dup for STDERR: $!"; 

保存和恢复文件之前和之后的处理...

事情是,如果没有从程序我最终输出与一个大小为0的文件,但我想没有文件。我怎么做,而不是手动检查和删除文件?

谢谢!

+5

以编程方式检查和删除文件。两条线是困难? – ikegami 2011-04-19 04:08:27

回答

5

你可以配合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; 
+1

只适用于PerlIO - 也就是Perl内的东西。不确定这个问题是仅针对perl还是针对子流程 - 也不完全清楚:-( – Tanktalus 2011-04-18 23:46:35

+1

如果PerlIO是所有需要的,则可以使用内存文件。 – ysth 2011-04-19 01:43:44

0

我能想到的唯一方法是分离一个通过管道发送回所有东西的子进程(认为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 

希望有所帮助。

5

只是在检查结束时,如果干啥g已被写入,如果没有,则删除该文件。确保你有自动刷新。

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; 
} 
+0

该OP特别指出“不诉诸于检查和删除文件“ - 这基本上是你在做什么? – Tanktalus 2011-04-19 02:44:17

+0

@Tanktalus - 手动过程是一个不自动的。手动检查和删除将是:运行过程; ls -l日志文件; rm日志文件。脚本底部的三行似乎比编写处理这一特定陷阱的特殊日志文件类的页面更省时。 – unpythonic 2011-04-19 14:19:34

0

你不想耽误打开文件,如果你推迟开放像一个权限错误,或缺少目录的任何问题在该文件的路径中将导致程序在第一个打印语句失败。鉴于它听起来像你可能有程序运行,从不打印任何东西,你可能会面对你的程序在未来的一些随机时间失败,因为它只是打印到一个文件,它无法打开数月。那时你或者你的继任者可能已经忘记了这个功能曾经存在过。

在完成之后检查文件是否更好,以查看它是否为空,如果是则将其删除。 如果你想为你做的话,你可以将逻辑包装在一个类中。

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 [email protected]; 
    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()。它也可以处理更好的错误。