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

What does local do on the standard streams / bareword word filehandles: STDIN?

我正在考虑做

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

不过,我想*STDIN恢复后,所以我试过了。

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

您可以使用 cat 尝试使用 local 关键字,像这样,

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

只有没有 local才会cat/dev/null读取。那么 local 在裸字文件句柄上实际上做了什么?

open ...,'-|'system 从标准输入读取的调用将尝试从文件描述符 0 读取。如果你不搞砸,外部程序将从相同的输入流读取作为 perl 的标准输入。

# reads from standard input
open my $fh, '-|', "/bin/cat";
while (<fh>) { print }

# reads from /tmp/foo
open STDIN, "<", "/tmp/foo";       # replaces fd0 with handle to /tmp/foo
open my $fh, '-|', "/bin/cat";
while (<$fh>) { print }

# reads from standard input
open local *STDIN, "<", "/tmp/foo";  # doesn't close fd0, creates new fd
open my $fh, '-|',  "/bin/cat";
while (<$fh>) { print }

# reads from /tmp/foo
close STDIN;
open FOO, "<", "/tmp/foo";       # fileno(FOO) should be 0 now
open my $fh, '-|', "/bin/cat";
while (<$fh>) { print }

有系统文件句柄(称为文件描述符,或“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,从fd 0读取,会因此注意到变化。

local *STDIN 创建 glob 的备份并将 *STDIN 与新的 glob 相关联。原来的 *STDIN 仍在内存中,因此没有释放与 *STDIN 关联的资源。这意味着与 *STDIN 关联的任何文件句柄仍处于打开状态。

当您使用 open(local *STDIN, ...) 时,新的 fd 将具有最低可用数量。 fd 0 仍在内存中的某处被原来的 *STDIN 使用,所以 fd 0 不可用。也许 fd 3 将是这次第一个可用的 fd(STDOUT 和 STDERR 使用 1 和 2)。

$ 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读取,会从原来的句柄读取。

Perl 可以使任何与 STDIN、STDOUT 和 STDERR 关联的句柄在执行 cat 之前变成 fd 0、1 和 2,但它没有。这个留给你了。


  1. 虽然我在描述 unixy 系统中的工作原理,但在 Windows.

    中的工作原理类似
  2. open传递了一个包裹了系统文件句柄的句柄,同时也在创建一个新的系统文件句柄的情况下,在unixy系统上使用的内部机制如下:

    1. 使用 open 创建了一个新的 fd。它将具有最低的可用数量。
    2. dup2用于创建与原fd编号相同的新fd的副本。
    3. open创建的fd已关闭

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