首页 > 解决方案 > 将文件句柄变量从 STDOUT 重新分配给没有 undef() 的文件时 Perl 的奇怪行为

问题描述

执行以下简化代码时:

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. 输出如下,这不是我所期望的。
标准输出 输出A.txt 输出B.txt
标准输出 1 档案一 文件 B
标准输出?2
标准输出 3

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

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

作为旁注:

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

标签: perlwarningsstdoutfilehandle

解决方案


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

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

所以第一张桌子人们应该期待的。

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

一些注意事项:

  • 需要研究的文档中的几页:openPlaying with STDIN and STDOUT (old perlpentut),并 在 perlfunc 中打开 FILEHANDLE

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

    或者,在一个单独的范围内(块会很好),local *STDOUT;然后所有提及的内容STDOUT都将与这个本地副本一起使用。一旦您离开范围,全局范围就会恢复。

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

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

  • “三个参数”open更好:(open my $fh, '<', $file ...和检查or die $!

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


†</sup>文件描述符 1,Perl 为其提供打开的STDOUT文件句柄(实际上是*STDOUT glob,但*在需要文件句柄时可以省略,或者作为适当的参考\*STDOUT

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

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

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

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


推荐阅读