提问人:mak 提问时间:2/25/2021 最后编辑:zdimmak 更新时间:6/18/2021 访问量:183
Perl 在没有 undef() 的情况下将 filehandle 变量从 STDOUT 重新赋值到文件中时的奇怪行为
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]
我遇到以下奇怪的行为:
- 打印(行 [18])到关闭(未打开)(行 [13])时,即使使用也不会发出警告。
$outputHandle
use warnings FATAL => 'unopened';
- 输出如下,这不是我所期望的。
标准输出 | 输出 .txt | 输出B.txt |
---|---|---|
标准输出 1 | 文件 A | 文件 B |
性病?2 | ||
标准输出 3 |
这是我期望的输出,假设第 [17] 行被注释掉并且不会引发warnings FATAL => 'unopened'
标准输出 | 输出 .txt | 输出B.txt |
---|---|---|
标准输出 1 | 文件 A | 文件 B |
性病?2 | ||
标准输出 3 | ||
标准输出 4 | ||
性病?5 |
顺便说一句:
- 默认情况下,原始程序输出到 STDOUT,但如果有参数传递给程序,则切换到输出到文件。
- 我正在使用“这是为 MSWin32-x64-multi-thread 构建的 perl 5,版本 28,subversion 1 (v5.28.1)”
答:
在第 [08] 行之前调用将完成输出工作。
但是,不能解决打印到第 [18] 行的已关闭(未打开)文件句柄时引发的未发出警告的问题。undef($outHandle)
undef($outHandle)
评论
my
open(my $outHandle, ">outputA.txt") or die ("A: $!\n");
当标准输出流†被重定向(重新打开)到文件时,就无法使用它打印到控制台;本来要去那里的东西现在连接到了该文件。因此,一旦完成,所有其他打印件都会以一种或另一种方式完成,最终出现在文件中。STDOUT
然后该文件句柄被关闭;在那之后,就无法再打印了。‡STDOUT
因此,第一张表是人们应该期待的。
打印到未打开的文件句柄时,我确实收到警告,因此对于任何和所有打印,在它关闭后。编辑 ...没有但启用了正常,这就是(我如何测试这个答案)。但是,仅使用该警告类别,就没有打印到已关闭的文件句柄(已初始化然后关闭的文件句柄)的警告。请参阅此页面。STDOUT
FATAL => 'unopened'
warnings
一些注意事项:
文档中的几页要学习:打开,并使用 STDIN 和 STDOUT(旧 perlopentut),并在 perlfunc 中打开 FILEHANDLE
有一些方法可以通过控制来操作标准流。一种是“复制”它,所以在它被重定向、使用和关闭后,人们可以恢复它。我想到的一些例子:在 STDOUT 和重定向的帖子中。(请注意,这会创建一个别名,因此当其中一个更改时,另一个也会更改。
$fh = \*STDOUT
或者,在一个单独的范围内(块会做得很好),做,然后所有提到的都将与这个本地副本一起使用。离开作用域后,全局作用域将恢复。
local *STDOUT;
STDOUT
或者你可以使用而不是弄乱本身。
select
STDOUT
其中大部分在 perl.com 文章中得到了很好的总结。有关更多信息,请参阅此页面
“三个论点”更好:(并检查
open
open my $fh, '<', $file ...
or die $!
)它被称为“句柄”,而不是“处理程序”
†文件描述符 1,Perl 为其提供了一个打开的文件句柄(实际上是 glob,但在需要文件句柄时可以省略,或者作为适当的引用STDOUT
*STDOUT
*
\*STDOUT
)
‡即使没有首先重定向,一旦关闭,就没有连接到标准输出流,也没有简单的方法可以像以前一样重新打开它。(当然,有一些方法可以把东西放在终端上。STDOUT
一般来说,关闭不是一个好主意,因为许多各方都希望它是开放的。首先,一旦 fd1 被腾空,其他东西可能会被分配,带来奇怪的麻烦(参见这篇文章和 Perl 错误 #23838)。如果你的程序(以某种方式)分叉,并且子进程继承了他们不可能期望的东西,该怎么办?下一行中可能调用的库怎么办?等。STDOUT
有更好的方法可以在文本中操纵、提及和链接。STDOUT
如果您需要离开,至少将其重定向到(在 Windows 上)而不是直接关闭它。STDOUT
/dev/null
nul
评论
open($outHandle, ">outputA.txt")
$outHandle
STDOUT
STDOUT
>
$outHandle
STDOUT
undef($outHandle)
open($outHandle, ">outputA.txt")
STDOUT
/dev/tty
$outHandle
STDOUT
STDOUT
local
select
*STDOUT
FATAL
评论
local *STDOUT
>&