将文件句柄变量从 STDOUT 重新分配给没有 undef() 的文件时,Perl 的奇怪行为 [英] Perl's odd behavior when reassigning a filehandle variable from STDOUT to a file without undef()

查看:36
本文介绍了将文件句柄变量从 STDOUT 重新分配给没有 undef() 的文件时,Perl 的奇怪行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

执行以下简化代码时:

使用严格;# [01]使用警告 FATAL =>'未开封';# [02]# [03]我的 ($inHandle, $outHandle) = (\*STDIN, \*STDOUT);# [04]打印 $outHandle "STDOUT 1\n";# [05]# [06]# $outHandle 重新分配给 outputA.txt ???# [07]open($outHandle, ">outputA.txt") 或 die ("A: $!\n");# [08]打印 $outHandle "FILE A\n";# [09]打印标准输出?2\n";# [10]打印标准输出STDOUT 3\n";# [11]关闭 $outHandle;# [12]# [13]# $outHandle 已关闭 # [14]打印标准输出STDOUT 4\n";# [15]打印标准输出?5\n";# [16]打印 $outHandle "FILE CLOSED\n";# [17]# [18]# $outHandle 重新分配给 outputA.txt ???# [19]open($outHandle, ">outputB.txt") 或 die ("B: $!\n");# [20]打印 $outHandle "FILE B\n";# [21]关闭 $outHandle;# [22]

我遇到以下奇怪的行为:

  1. 打印(第 [18] 行)到关闭(未打开)的 $outputHandle(第 [13] 行)时不会发出警告,即使 使用警告 FATAL =>'unopened'; 被使用.
  2. 输出如下,这不是我所期望的.

<头>
标准输出outputA.txtoutputB.txt
标准输出 1文件A文件 B
标准输出?2
标准输出 3

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

<头>
标准输出outputA.txtoutputB.txt
标准输出 1文件A文件 B
标准输出?2
标准输出 3
标准输出 4
标准输出?5

附注:

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

解决方案

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

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

所以第一个表应该是什么.

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

一些注意事项:

  • 要研究的文档中的几页:open,以及玩转标准输入和标准输出(老perlopentut),和在 perlfunc 中打开 FILEHANDLE

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

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

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

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

  • 三论"open 更好:open my $fh, '<', $file ... (and check or die $!)

  • 它被称为句柄,"不是处理程序"


文件描述符 1,为此 Perl 提供了一个打开的 STDOUT 文件句柄(实际上是 *STDOUT glob,但 * 在需要文件句柄时可以省略,或者作为适当的引用 \*STDOUT)

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

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

有更好的方法来操作 STDOUT,文中提到并链接.

如果你需要 STDOUT 消失,至少将它重定向到 /dev/null (nul在 Windows 上)而不是完全关闭它.

When executing the following simplified code:

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]

I encounter these following odd behaviors:

  1. No warning is raised when printing (line [18]) to a closed (unopened) $outputHandle (line [13]), even when use warnings FATAL => 'unopened'; is used.
  2. The output is as follows which is not what I expect.

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

This is the output I expect assuming line [17] is commented out and doesn't raise the warnings FATAL => 'unopened'

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

As a side note:

  1. The original program outputs to STDOUT as default but switches to outputting to a file if there is a parameter passed to the program.
  2. I am using "This is perl 5, version 28, subversion 1 (v5.28.1) built for MSWin32-x64-multi-thread"

解决方案

When the standard output stream gets redirected (reopened) to a file then there is no way to print to the console with it; what was meant to go there is now connected to that file instead. So once that was done then all other prints to STDOUT, done one way or another, wind up in the file.

And then that filehandle gets closed; after that one cannot print to STDOUT anymore.

So the first table is what one should expect.

I do get a warning when printing to an unopened filehandle, so for any and all prints to STDOUT after it got closed.   Edit ... without FATAL => 'unopened' but with normal warnings enabled, that is (how I tested for this answer). However, with that warning category alone there are no warnings for printing to closed filehandles (ones that had been initialized then closed). See this page.

Some notes:

  • A few pages in docs to study: open, and Playing with STDIN and STDOUT (old perlopentut), and open FILEHANDLE in perlfunc

  • There are ways to manipulate standard streams with control. One is to "dup" (duplicate) it, so after it's been redirected, used, and closed one can restore it. Some examples that come to mind: in posts on STDOUT and on redirection. (Note that $fh = \*STDOUT creates an alias, so when one of them is changed so is the other.)

    Or, in a separate scope (block will do nicely), do local *STDOUT; and after that all mention of STDOUT will work with this local copy. Once you leave the scope the global one gets restored.

    Or you can use select instead of messing with the STDOUT per se.

    Most of these are nicely summarized in a perl.com article. For more also see this page

  • "Three argument" open is better: open my $fh, '<', $file ... (and check or die $!)

  • It's called a "handle," not a "handler"


The file descriptor 1, for which Perl provides an opened STDOUT filehandle (really *STDOUT glob, but * may be omitted when a filehandle is expected, or as a proper reference \*STDOUT)

Even if STDOUT hasn't been first redirected, once it is closed there is no connection to standard output stream, and there is no simple way to reopen it as it had been. (There's of course ways to put things out on the terminal.)

In general, closing STDOUT isn't a good idea, since many parties expect it to be open. For one, once fd1 is vacated other things may get it assigned, with bizarre trouble (see this post and Perl bug #23838). What if your program forks (in some way), and children processes inherit what they cannot possibly expect? What with the libraries that may get called in the next line? Etc.

There are better ways to manipulate STDOUT, mentioned and linked in the text.

If you need STDOUT to be gone, at least redirect it to /dev/null (nul on Windows) instead of outright closing it.

这篇关于将文件句柄变量从 STDOUT 重新分配给没有 undef() 的文件时,Perl 的奇怪行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆