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

mak*_*mak 1 perl warnings stdout filehandle

执行以下简化代码时:

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]
Run Code Online (Sandbox Code Playgroud)

我遇到以下奇怪的行为:

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

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

标准输出 输出A.txt 输出B.txt
标准输出 1 文件A 文件乙
标准输出?2
标准输出 3
标准输出 4
标准输出?5

作为旁注:

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

zdi*_*dim 6

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

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

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

打印到未打开的文件句柄时,我确实收到警告,因此对于STDOUT关闭后的任何和所有打印。   编辑...没有FATAL => 'unopened'但正常warnings启用,即(我如何测试此答案)。但是,仅使用该警告类别,就没有打印到已关闭文件句柄(已初始化然后关闭的文件句柄)的警告。请参阅此页面

一些注意事项:

  • 文档中的几页要学习:open , and Playing with STDIN and STDOUT (old perlopentut) , and open FILEHANDLE in perlfunc

  • 有一些方法可以通过控制来操作标准流。一种是“ dup ”(复制)它,以便在它被重定向、使用和关闭后可以恢复它。想到的一些例子:在关于 STDOUT关于重定向的帖子中。(请注意,这$fh = \*STDOUT会创建一个别名,因此当其中一个更改时,另一个也会更改。)

    或者,在一个单独的范围内(块会做得很好),这样做local *STDOUT;之后所有提到的STDOUT都将与这个本地副本一起工作。一旦离开范围,全局范围就会恢复。

    或者您可以使用select而不是弄乱STDOUT本身。

    其中大部分都在perl.com 文章中得到了很好的总结。有关更多信息,请参阅此页面

  • “三个论点”open更好:(open my $fh, '<', $file ...并检查or die $!

  • 它被称为“句柄”,而不是“句柄”


文件描述符1,为此,Perl提供一个打开STDOUT的文件句柄(真*STDOUT glob的,但是*当一个文件句柄,预计可以被省略,或作为一个适当的参考\*STDOUT

即使STDOUT没有首先被重定向,一旦它关闭,就没有与标准输出流的连接,也没有像以前那样重新打开它的简单方法。(当然有办法把东西放在终端上。)

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

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

如果您需要 STDOUT离开,至少将其重定向到/dev/nullnul在 Windows 上)而不是完全关闭它。