2011-04-18 105 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()。它也可以處理更好的錯誤。