将文件句柄变量从 STDOUT 重新分配给没有 undef() 的文件时 Perl 的奇怪行为

Perl's odd behavior when reassigning a filehandle variable from STDOUT to a file without undef()

执行以下简化代码时:

use strict;                                          # [01]
use warnings FATAL => 'unopened';                    # [02]
                                                     # [03]
my ($inHandle, $outHandle) = (\*STDIN, \*STDOUT);    # [04]
print $outHandle "STDOUT  1\n";                      # [05]
                                                     # [06]
# $outHandle re-assigned to outputA.txt ???          # [07]
open($outHandle, ">outputA.txt") or die ("A: $!\n"); # [08]
print $outHandle "FILE A\n";                         # [09]
print             "STDOUT? 2\n";                     # [10]
print STDOUT      "STDOUT  3\n";                     # [11]
close $outHandle;                                    # [12]
                                                     # [13]
# $outHandle is closed                               # [14]
print STDOUT      "STDOUT  4\n";                     # [15]
print             "STDOUT? 5\n";                     # [16]
print $outHandle "FILE CLOSED\n";                    # [17]
                                                     # [18]
# $outHandle re-assigned to outputA.txt ???          # [19]
open($outHandle, ">outputB.txt") or die ("B: $!\n"); # [20]
print $outHandle "FILE B\n";                         # [21]
close $outHandle;                                    # [22]

我遇到了以下这些奇怪的行为:

  1. 打印(第 [18] 行)到关闭(未打开)$outputHandle(第 [13] 行)时,即使使用 use warnings FATAL => 'unopened'; 也不会引发警告。
  2. 输出如下,这不是我所期望的。
STDOUT outputA.txt outputB.txt
STDOUT 1 FILE A FILE B
STDOUT? 2
STDOUT 3

这是我期望的输出,假设第 [17] 行被注释掉并且没有引发 warnings FATAL => 'unopened'

STDOUT outputA.txt outputB.txt
STDOUT 1 FILE A FILE B
STDOUT? 2
STDOUT 3
STDOUT 4
STDOUT? 5

旁注:

  1. 原始程序默认输出到 STDOUT,但如果有参数传递给程序,则切换到输出到文件。
  2. 我正在使用“这是为 MSWin32-x64-多线程构建的 perl 5,版本 28,subversion 1 (v5.28.1)”

在行 [08] 之前调用 undef($outHandle) 将完成输出工作。
但是,undef($outHandle) 无法解决在第 [18] 行打印到已关闭(未打开)文件句柄时引发的无警告问题。

当标准输出流 被重定向(重新打开)到一个文件时,就无法用它打印到控制台;本来要去那里的东西现在连接到那个文件了。因此,一旦完成,所有其他打印到 STDOUT,以一种或另一种方式完成,在文件中结束。

然后那个文件句柄被关闭;在那之后就不能再打印到 STDOUT

所以第一个 table 人们应该期待的。

我在打印到未打开的文件句柄时确实收到警告,因此对于 STDOUT 关闭后的所有打印。 Edit ... 没有 FATAL => 'unopened' 但启用了正常的 warnings,即(我如何测试此答案)。但是,单独使用该警告类别 没有 打印到 已关闭 文件句柄(已初始化然后关闭的文件句柄)的警告。参见

一些注意事项:

  • 要研究的文档中的几页:open,以及 Playing with STDIN and STDOUT (old perlopentut),以及 open FILEHANDLE in perlfunc

  • 有很多方法可以通过控制来操纵标准流。一个是“dup" (duplicate) it, so after it's been redirected, used, and closed one can restore it. Some examples that come to mind: in posts and 。(请注意,$fh = \*STDOUT 创建了一个别名,因此当其中一个更改时,另一个也会更改。)

    或者,在一个单独的范围内(块会很好地完成),执行 local *STDOUT; 然后所有提到的 STDOUT 将适用于这个 local 副本。一旦你离开范围,全局范围就会恢复。

    或者你可以使用 select 而不是搞乱 STDOUT 本身。

    其中大部分在 perl.com article. For more also see this page

    中得到了很好的总结
  • “三参数”open更好:open my $fh, '<', $file ...(并检查or die $!

  • 它被称为“handle”,而不是“处理程序”


file descriptor 1, for which Perl provides an opened STDOUT filehandle (really *STDOUT glob,但是 * 可以在需要文件句柄时省略,或者作为适当的引用 \*STDOUT)

即使 STDOUT 没有被第一次重定向,一旦它 关闭 就没有连接到标准输出流,并且没有简单的方法可以像以前一样重新打开它。 (当然有一些方法可以把东西放到终端上。)

一般来说,关闭 STDOUT 不是一个好主意,因为很多人都希望它是开放的。首先,一旦 fd1 被腾出,其他东西可能会被分配,这会带来奇怪的麻烦(参见 this post and Perl bug #23838)。如果您的程序分叉(以某种方式),并且子进程继承了他们不可能期望的东西怎么办?下一行可能调用的库怎么办?等等

有更好的方法来操纵 STDOUT,在文中提到和链接。

如果你需要 STDOUT离开,至少将它重定向到/dev/nullnul on Windows)而不是直接关闭它。