系统文件有一个句柄(称为文件描述符或 "fd"),而Perl文件也有一个句柄。Perl文件通常包装一个系统文件,但并非总是如此。例如,open(my $fh, '<', \$buf)
创建一个与任何系统文件句柄都不相关联的 Perl 句柄。
其他进程不知道您进程的变量,因此它们不知道任何关于 Perl 文件句柄的事情。无论哪个句柄作为 fd 0 打开,都将用作标准输入(STDIN),fd 1 作为标准输出(STDOUT),fd 2 作为标准错误(STDERR).[1]
当 open
接收到一个现有的 Perl 句柄时,该句柄将被关闭。如果创建一个新的系统文件句柄,则它将被赋予与原始 fd 相同的编号(如果原始句柄有)或最低可用编号(如果没有)。[2]
因此,当您使用 open(*STDIN, ...)
时,与 *STDIN{IO}
关联的新句柄也将是 fd 0。
$ perl -e'
CORE::say fileno(*STDIN);
open(*STDIN, "<", "/dev/null") or die $!;
CORE::say fileno(*STDIN);
'
0
0
cat
命令从文件描述符0(即标准输入)读取数据,因此会注意到变化。
local *STDIN
创建了一个全局变量的备份和相关的*STDIN
,并重新创建了全新的全局变量。原始的*STDIN
仍然在内存中,因此没有与*STDIN
关联的资源被释放。这意味着任何与*STDIN
关联的文件句柄仍然是打开状态。
当您使用open(local *STDIN, ...)
时,新的文件描述符将具有可用的最低号码。文件描述符0仍然被原始的*STDIN
在内存中使用,因此文件描述符0不可用。也许这一次文件描述符3将是第一个可用的文件描述符(1和2已经被STDOUT和STDERR使用了)。
$ perl -e'
CORE::say fileno(*STDIN);
{
open(local *STDIN, "<", "/dev/null") or die $!;
CORE::say fileno(*STDIN);
}
CORE::say fileno(*STDIN);
'
0
3
0
cat
,从fd 0读取,将从原始句柄读取。
在执行
cat
之前,Perl可以使与STDIN、STDOUT和STDERR关联的所有句柄成为fd 0、1和2,但它没有这样做。这留给你自己去完成。
- 尽管我在描述unixy系统的工作原理,但Windows的情况也类似。
- 当
open
传递包装系统文件句柄的句柄并且还创建了一个新的系统文件句柄的情况下,在unixy系统上使用的内部机制如下:
- 使用
open
创建一个新的fd。它会有最低可用的编号。
dup2
用于创建具有与原始fd相同编号的新fd的副本。
- 由
open
创建的fd被关闭。
这意味着如果发生错误,原始fd不会关闭。
open(*STDIN, "file")
时,Perl 首先会打开 "file",然后将返回的文件描述符 dup2(2) 到 fd 0 中。首先关闭与 stdin 关联的文件描述符是没有意义的,因为打开新文件可能会失败。实际上,Perl 并没有这样做,一个简单的 strace 或源代码检查就可以证明这一点。 - user10678532