本地在标准流/裸字文件句柄上做什么:STDIN?

Eva*_*oll 6 perl stdin scope filehandle

我正在考虑做

open(*STDIN, "<", "/dev/null" );
open(my $fh, "-|", "/bin/bash", "/tmp/foo");
print for <$fh>;'
Run Code Online (Sandbox Code Playgroud)

但是,我想*STDIN恢复,之后,所以我尝试了。

{
  open(local *STDIN, "<", "/dev/null" );
  open(my $fh, "-|", "/bin/bash", "/tmp/foo");
  print for <$fh>;
}
Run Code Online (Sandbox Code Playgroud)

您可以尝试cat使用和不使用local这样的关键字,

{
  # remove local and it works,
  open(local *STDIN, "<", "/dev/null" );
  open(my $fh, "-|", "/bin/cat");
  print for <$fh>;
}
Run Code Online (Sandbox Code Playgroud)

只有没有 local才会cat/dev/null. 那么local在一个裸字文件句柄上实际上做了什么呢?

ike*_*ami 4

有系统文件句柄(称为文件描述符,或“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
Run Code Online (Sandbox Code Playgroud)

cat,从 fd 0 读取,因此会注意到变化。

local *STDIN创建 glob 的备份并*STDIN与新的 glob 关联。原始数据*STDIN仍在内存中,因此不会*STDIN释放任何关联的资源。这意味着与之关联的任何文件句柄*STDIN仍然处于打开状态。

当您使用 时open(local *STDIN, ...),新的 fd 将具有可用的最小编号。fd 0 仍在内存中的某处被原始文件使用*STDIN,因此 fd 0 不可用。也许这次 fd 3 将是第一个可用的 fd(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
Run Code Online (Sandbox Code Playgroud)

cat,从 fd 0 读取,将从原始句柄读取。

Perl 可以使与 STDIN、STDOUT 和 STDERR 关联的任何句柄在执行之前变成 fd 0、1 和 2 cat,但事实并非如此。这个就留给你了。


  1. 虽然我描述了 Unix 系统中的工作原理,但 Windows 中的工作原理类似。

  2. 在传递一个包装系统文件句柄的句柄并且还创建新的系统文件句柄的情况下open,unixy系统上使用的内部机制如下:

    1. 使用创建一个新的文件描述符open。它将具有最低的可用数量。
    2. dup2用于创建新 fd 的副本,其编号与原始 fd 相同。
    3. 创建的 fdopen被关闭。

    这意味着如果发生错误,原始 fd 不会关闭。