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

发布于 2024-11-01 20:18:41 字数 315 浏览 1 评论 0原文

我在 perl 脚本中重定向 STDOUT 和 STDERR ,方法是:

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

保存和恢复之前和之后的文件句柄...

事情是,如果程序没有输出,我最终会得到一个大小为 0 的文件,但我希望有根本没有文件。如何在不手动检查和删除文件的情况下做到这一点?

谢谢!

I'm redirecting STDOUT and STDERR in a perl script with:

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

saving and restoring the file handles before and after ...

Thing is, if there's no output from the program I end up with a size 0 file but I'd like to have no file at all. How can I do that without resorting to checking and deleting the file manually?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

記憶穿過時間隧道 2024-11-08 20:18:41

您可以将 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;

You could tie STDOUT to a class that delays opening of the destination file until the first time the handle is written to:

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;

Then in your main program, you'd say:

tie *STDOUT, 'FastidiousHandle', '>', $path;
my $saved_stderr = *STDERR;
*STDERR = *STDOUT;

To restore the previous handles you'd say:

*STDERR = $saved_stderr;
untie *STDOUT;
野生奥特曼 2024-11-08 20:18:41

只需检查最后是否写入了任何内容,如果没有,则删除该文件。确保您已打开自动冲洗功能。

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

Just check at the end if anything has been written, and if not, remove the file. Make sure you have autoflush on.

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

Or in the old style...

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;
}
装纯掩盖桑 2024-11-08 20:18:41

我能想到的唯一方法是分叉一个子进程,该子进程通过管道发送回所有内容(想想 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

希望有帮助。

The only way I can think of is to fork off a subprocess which sends back everything via pipe (think IO::Pipe or something like IPC::Open2 - either way, you still redirect your STDERR to STDOUT in the child), and then in the parent, write the stuff you get in the pipe to the log file - this allows you to open the logfile when you first have data. For example:

#!/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 /));

When I run this, I get:

$ ls /tmp/*.out
ls: cannot access /tmp/*.out: No such file or directory
$ cd tmp
$ perl foo.pl
$ ls /tmp/*.out
/tmp/ls.out

Hope that helps.

许久 2024-11-08 20:18:41

您不想延迟打开文件,如果确实延迟打开,任何问题(例如权限错误或文件路径中缺少目录)都会导致程序在第一个打印语句处失败。鉴于听起来您的程序运行可能永远不会打印任何内容,您可能会在未来的某个随机时间面临程序失败,因为它恰好打印到几个月内无法打开的文件。到那时,您或您的继任者可能已经忘记了此功能的存在。

最好在完成后检查该文件,看看它是否为空,如果是则将其删除。
如果您想为您执行此操作,可以将逻辑包装在一个类中。

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() 的所有方式,并且它还应该覆盖对 的调用$file_obj->open() 的方式与 new 类似。它还可以提供更好的错误处理。

You don't want to delay opening the file, if you do delay the open any problems like a permission error, or a missing directory in the path to the file will cause the program to fail at the first print statement. Given that it sounds like you might have program runs that never print anything you could likely face your program failing at some random time in the future because it just happened to print to a file that it couldn't open for months. By then you, or your successor might have forgotten this feature ever existed.

It's much better to check the file after your done to see if it's empty and remove it if it is.
You can wrap the logic in a class if you want to do it for you.

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";

This code isn't really production ready, it doesn't try to handle all the ways IO::File->new() can be called, and it should also override calls to $file_obj->open() in a similar manner to new. It also could do with better error handling.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文