【问题标题】:What type is STDOUT, and how do I optionally write to it?STDOUT 是什么类型,我如何选择写入它?
【发布时间】:2026-02-11 07:15:01
【问题描述】:

STDOUT 有“类型”吗?

printf STDERR ("STDOUT = %s\n", STDOUT);
printf STDERR ("\*STDOUT = %s\n", *STDOUT);
printf STDERR ("\\\*STDOUT = %s\n", \*STDOUT);

生产:

STDOUT = STDOUT
*STDOUT = *main::STDOUT
\*STDOUT = GLOB(0x600078848)

我了解*main::STDOUTGLOB(0x600078848) 条目。 “裸词”让我很好奇。

我问是因为我想将一个类似文件句柄的参数传递给方法调用。在“C”中,我会使用文件描述符或文件 *。我希望它默认为 STDOUT。我所做的是:

$OUT_FILE_HANDLE = \*STDOUT;
if(@ARGV > 0 ) {
    open($OUT_FILE_HANDLE, ">", "$ARGV[0]") or die $!;
}

它有效,但我不知道我做了什么。我搞砸了STDOUT吗?我怀疑我已经“破坏”(覆盖)了 STDOUT,这不是我想要的。

请原谅复合问题;他们似乎是相关的。

【问题讨论】:

  • printf STDERR ("STDOUT = %s\n", STDOUT); 中的STDOUT 是“诗歌优化”兼容性错误功能的一个示例——STDOUT 裸字被视为文字字符串,与"STDOUT" 相同。这是use strict 的错误,您应该始终使用它。至于您声称理解 typeglobs 和句柄 - 非常适合您!我总是必须考虑它们是如何在解释器中实现的,以便理解混乱。

标签: perl stdout filehandle


【解决方案1】:

来自perlvar

以数字或标点字符开头的 Perl 标识符不受 package 声明的影响,并且始终强制包含在包 main 中;它们也免于出现strict 'vars' 错误。其他一些名称也可以通过这些方式获得豁免:[...] STDOUT

所以,STDOUT 是一个包含预先打开的文件句柄的全局变量。

来自perlfunc

如果 FILEHANDLE 是未定义的标量变量(或数组或哈希元素),则自动激活新的文件句柄,这意味着该变量被分配了对新分配的匿名文件句柄的引用。否则如果 FILEHANDLE 是一个表达式,它的值就是真正的文件句柄。

您的$OUT_FILE_HANDLE 不是未定义的,因此打开的是它的值STDOUT。 AFAIK,如果你打开一个已经打开的句柄,它会先隐式关闭。

有几种方法可以做你想做的事。从上面的引用中可以明显看出第一点——不要在open 之前定义$OUT_FILE_HANDLE

if (@ARGV > 0 ) {
  open($OUT_FILE_HANDLE, ">", "$ARGV[0]") or die $!;
} else {
  $OUT_FILE_HANDLE = \*STDOUT;
}
# do stuff to $OUT_FILE_HANDLE

另一种是使用select,这样就不需要传递文件句柄了:

if (@ARGV > 0 ) {
  open($OUT_FILE_HANDLE, ">", "$ARGV[0]") or die $!;
  select $OUT_FILE_HANDLE;
}
# do stuff (without specifying a file handle)
select STDOUT;

【讨论】:

  • select 可能是更多 perl/lisp 要做的事情。我也在考虑一个动态范围的STDOUT。这可能是 select 的实现方式。谢谢。
【解决方案2】:

创建一个词法文件句柄作为STDOUT 的副本并根据需要对其进行操作

sub manip_fh { 
    my ($fh) = @_;
    say $fh "hi";                              # goes to STDOUT
    open my $fh, '>', 'a_file.txt' or die $!;  # now it's to a file
    say $fh "hello";
}

open my $fh, '>&', STDOUT;  # via dup2

manip_fh($fh);

say "hi";  # still goes where STDOUT went before being dup-ed (terminal)

这个新的、独立的文件句柄可以重新打开到另一个资源,而不影响STDOUT。见open

问题中的$OUT_FILE_HANDLE = \*STDOUT; 创建了一个别名,因此STDOUT 在“新”更改时确实会更改。你可以通过打印typeglob来查看

our $NEW = \*STDOUT;
say *{$main::NEW};    #--> *main::STDOUT

或从符号表中打印IO

say for *{$main::NEW}{IO}, *{$main::{STDOUT}}{IO};

并看到(对象字符串化为)相同(例如IO::File=IO(0x1a8ca50))。

当它使用open 和模式>& 被欺骗时,如第一个代码sn-p(但作为全局our)它打印*main::NEW,其IO::File 对象与@ 不同987654337@。 (将其设为全局 our 以便它在符号表中用于此检查,但不能真正使用; 最好有一个词法。)

【讨论】:

    【解决方案3】:

    您的这部分问题没有得到回答:

    “裸词”让我很好奇。

    没有其他含义的标识符是自己产生的字符串文字。[1]例如,foo'foo' 相同。

    $ perl -e'my $x = foo; print "$x\n";'
    foo
    

    这很容易出错,所以我们使用use strict qw( subs ); 来防止这种情况发生。

    $ perl -e'use strict; my $x = foo; print "$x\n";'
    Bareword "foo" not allowed while "strict subs" in use at -e line 1.
    Execution of -e aborted due to compilation errors.
    

    1. 请参阅this 了解 Perl 可以分配的其他含义。

    【讨论】:

    • 我以为它有时被解释为sub?我不知道,因为我总是use strict。为了这个例子,我不得不把它拿出来,看看会发生什么。
    • 如果先前声明了具有该名称的子,则将其解释为子调用。这是我提到的“其他含义”之一。
    • 我还有一个问题,但我不知道如何表达。是否有一种编程方式来查看/了解编译器将如何解释一个裸词?在我上面的程序中,我尝试打印 STDOUT,它达到了 #8,而不是 #7。我想。
    • 您可以使用-MO=Concise-MO=Concise,-exec查看程序是如何编译的。 /// 该列表是为此答案创建的原始内容。可能有错误,但由于广泛的测试,我很有信心。 (如果有人认为 Perl 对标识符的解释与此图表不同,我非常想听听它,以便我可以修复图表)。 /// 如果你指的是printf STDERR ("STDOUT = %s\n", STDOUT);, printf: #5, STDERR: #7, STDOUT: #8
    • 4的两个例子完全不同,只有一个会覆盖5。比较perl -le 'use constant length => 7; print length'(无法覆盖length)和perl -le 'BEGIN { package P; *{main::length} = sub(){ 17 } } print length'(手工导出器,覆盖length,常量折叠就像use constant)。