尝试打印到已关闭的文件句柄时没有错误或警告

No error or warning for trying to print to an already closed filehandle

在下面的小代码中,第 [09] 和 [18] 行没有出现错误或警告。我得到的唯一警告是行 [21]:

use strict;                                              # [01]
use warnings FATAL => 'unopened';                        # [02]
                                                         # [03]
open(my $outHandleA, ">outputA.txt") or die ("A: $!\n"); # [04] Opened $outHandleA
print $outHandleA "FILE A\n";                            # [05]
close $outHandleA;                                       # [06] Closed $outHandleA
                                                         # [07]
print $outHandleA;                                       # [08]
print $outHandleA "ABC\n";                               # [09] <---
print $outHandleA;                                       # [10]
print "-----";                                           # [11]
                                                         # [12]
open(OUT, ">outputB.txt") or die ("B: $!\n");            # [13] Opened OUT
print OUT "FILE B\n";                                    # [14]
close OUT;                                               # [15] Closed OUT
                                                         # [16]
print OUT;                                               # [17]
print OUT "DEF\n";                                       # [18] <---
print OUT;                                               # [19]
                                                         # [20]
print DOES_NOT_EXIST_HANDLE "GHI\n";                     # [21] Raises a FATAL warning
                                                         # [22]
print "JKL";                                             # [23] Does not reach here (as expected)

但是,第 [09] 和 [18] 行不应该也引发如下错误或警告,因为它已关闭(未打开)吗?

这可能是我的 Perl 版本的问题,它是“为 MSWin32-x64-多线程构建的 perl 5,版本 28,颠覆 1 (v5.28.1)”。此外,这是我在下面得到的程序的输出:

outputA.txt outputB.txt STDOUT
FILE A FILE B GLOB(0xfeb428)GLOB(0xfeb428)-----

上面 table 中的 STDOUT 输出来自第 [08]、[10] 和 [11] 行。请注意,上面 table 中括号 (...) 之间的值可能会随着每次执行而改变。

已经打开(初始化)并关闭的文件句柄与根本没有打开的文件句柄是有区别的;尝试向他们打印会引发不同的警告。 'unopened' entries in perldiag 之一说

%s() on unopened %s
(W unopened) An I/O operation was attempted on a filehandle that was never initialized. You need to do an open(), a sysopen(), or a socket() call, or call a constructor from the FileHandle package.

print() on closed filehandle 的条目说

print() on closed filehandle %s
(W closed) The filehandle you're printing on got itself closed sometime before now. Check your control flow.

虽然他们都收到了警告。

单独启用 use warnings FATAL => 'unopened' 时打印到已初始化但随后关闭的文件句柄不会收到警告。

use warnings;
#use warnings FATAL => 'unopened';
use strict;
use feature 'say';

open my $stdout, '>&', *STDOUT;    # a copy

say $stdout "hi";
close $stdout;
say $stdout "to lexical, closed";  # it warns (l.10)

FATAL_warnings_unopened: { 
    no warnings;                   # change to FATAL for 'unopened'
    use warnings FATAL => 'unopened';

    say $stdout "with FATAL to closed fh";      # no warning
    close STDOUT;
    say "with FATAL to STDOUT";                 # no warning

    say NON_EXISTENT_FH "no such filehandle!";  # l.20
};

say STDERR 'done';

这会打印

hi
say() on closed filehandle $stdout at warnings_FATAL.pl line 10.
say() on unopened filehandle NON_EXISTENT_FH at warnings_FATAL.pl line 20.

根据 FATAL 和块内的 unopened 文件句柄 NON_EXISTENT_FH,它确实退出了,但是没有关于在其正上方打印到 $stdout 的警告。第一个这样的打印有一个警告。刚刚关闭的 STDOUT 的打印也不会收到警告。

如果我们在最开始取消注释 use warnings FATAL... 行(并删除其他 warnings 行),则根本不会发出打印到关闭句柄的警告。

仔细阅读 warnings pragma 的文档很有帮助。简而言之,我建议始终先单独使用 use warnings;,然后为要创建的类别添加一个语句 FATAL(可以在同一语句中添加多个类别)。

您 运行 与 use 的细节及其背后的导入机制发生冲突。即,如果您 use Foo 'bar';,那么 Foo::bar 将被导入,即使 Foo 通常导出其他内容。

同理,use warnings FATAL => 'unopened'; 转所有警告。它只打开 'unopened' 警告:

$ perl -E 'use warnings FATAL => "unopened" ; print $x;'
$ perl -E 'use warnings; use warnings FATAL => "unopened" ; print $x;'
Name "main::x" used only once: possible typo at -e line 1.
Use of uninitialized value $x in print at -e line 1.