如何从Perl的STDOUT中取消联合效果?

时间:2009-03-17 19:45:42

标签: perl

当我运行时:

open FP, ">xyz";

my $file = *FP;

printf $file "first\n";

$file = *STDOUT;

printf $file "second\n";

open $file, ">abc";

print $file "third\n";

print STDOUT "fourth\n";

print FP "fifth\n";

“第四次”打印不会转到STDOUT,而是转到“abc”。

STDOUT与FP的不同之处在于行为符合预期。

我做错了什么?我不理解的是什么?

6 个答案:

答案 0 :(得分:11)

嗯,对于初学者来说,你错误地使用'开放'。

open my $fp , '>', 'xyz' ;

是推荐的语法。

你强烈建议你使用裸露的'FP',因为它不是词汇。

其次,你将文件指针重新打开为新事物。这不是很好的做法,它应该不是问题,但它只是一个坏主意。你应该关闭文件指针或让它超出范围(通过词法)。

第三,'* STDOUT'是一个参考。

my $fh = *STDOUT; 
print "$fh\n";   #prints  '*main::STDOUT';

所以当你这样做时:

open $fh, '>abc'; 
你正在做什么

open *STDOUT, '>abc'; 

如果你之后立即做

print "$fh\n"; 

你会注意到它仍会打印*main::STDOUT;

一些有趣的代码片段清除了这一点:

my $fh = *STDOUT;
open $fh, '<', "foo.txt"; 
print $fh "hello";
# Filehandle STDOUT opened only for input at (eval 288) line 6.

my $fh = *STDIN;
open $fh, '<', "foo.txt"; 
print <>; 
# contents of foo.txt here 

以下是推荐使用open的方法:

sub foo { 
    my $fh;
    open $fh , '<', 'file.txt' or Carp::croak('Cannot Open File.txt'); 
    # do stuff with $fh; 
    close $fh or Carp::carp('Something annoying in close :S '); 
}

请注意,如果省略关闭,只要$ fh超出可见度,文件就会关闭。

答案 1 :(得分:6)

如果我理解你要做什么,我想你想使用select。它允许您轻松切换文件句柄。您还应该使用更现代的open形式(3个参数,词法文件句柄和错误检查)。请参阅perldoc perlopentut

#!/usr/bin/env perl
use strict;
use warnings;

open my $fh,  '>', 'abc' or die "Can't open 'abc': $!";
open my $fh2, '>', 'def' or die "Can't open 'def': $!";
open my $fh3, '>', 'xyz' or die "Can't open 'xyz': $!";

select $fh;
print "First\n";

# Later
select $fh2;
print "Second\n";

# Later
select $fh3;
print "Third\n";

# Later
select STDOUT;
print "Fourth\n";

答案 2 :(得分:4)

您需要保存STDOUT才能恢复它。这来自open的... perldoc:

这是一个使用各种方法保存,重定向和恢复STDOUT和STDERR的脚本:

#!/usr/bin/perl
open my $oldout, ">&STDOUT"     or die "Can't dup STDOUT: $!";
open OLDERR,     ">&", \*STDERR or die "Can't dup STDERR: $!";

open STDOUT, '>', "foo.out" or die "Can't redirect STDOUT: $!";
open STDERR, ">&STDOUT"     or die "Can't dup STDOUT: $!";

select STDERR; $| = 1;  # make unbuffered
select STDOUT; $| = 1;  # make unbuffered

print STDOUT "stdout 1\n";  # this works for
print STDERR "stderr 1\n";  # subprocesses too

open STDOUT, ">&", $oldout or die "Can't dup \$oldout: $!";
open STDERR, ">&OLDERR"    or die "Can't dup OLDERR: $!";

print STDOUT "stdout 2\n";
print STDERR "stderr 2\n";

查看“或死”部分。你应该总是测试一个开放的错误...

答案 3 :(得分:3)

本地化STDOUT。然后,您可以在有限的动态范围内对其进行别名。离开该范围后,STDOUT将恢复正常。

use strict;
use warnings;

print_stuff('Normal STDOUT');

{   local *STDOUT;
    open( STDOUT, '>', 'out' ) 
         or die "Can't redirect STDOUT: $!";
    local $| = 1;  # Unbuffer handle. 
                   # Do this AFTER redirecting STDOUT.

    print_stuff('Aliased to out');

    sleep 10;
} 


print_stuff('Back to normal STDOUT');

sub print_stuff {
    print join "\n", @_, '';
}

答案 4 :(得分:1)

当您执行打开$ file,“&gt; abc”时,您从根本上操作的是STDOUT的文件描述符,您现在已将其重新打开到另一个目标。也就是说,当你执行打开时,你的两个别名都在一个被重新利用的底层资源上运行,而不管用于引用它的别名。

答案 5 :(得分:0)

如果我将在短时间内使用文件句柄,我会把它放在一个匿名块中。这会自动限制任何非全局变量的范围。

{
  open my $fh, '>', 'abc';
  print $fh "assorted data";
  close $fh;
}

作为额外的好处,您甚至不需要拥有“close $fh;”行,因为当变量超出范围时它会自动关闭。

如果要使用此技术,则应始终本地化在代码块内修改的任何全局变量。

{
  local *FH;
  open FH, '>', 'abc';
  print FH "assorted data";
  close FH;
}