在我的Perl脚本中,我希望处理来自STDIN
或给定文件(如果指定)的行,这与Linux/UNIX命令行实用程序一样。
为此,我在我的脚本中有以下部分(为帖子简化):
use strict;
use warnings;
my $in = \*STDIN;
open $in, '<', $ARGV[0] or die if (defined $ARGV[0]);
print while (<$in>);
实际上,我将$in
定义为对STDIN
typeglob的引用,因此通常情况下,如果没有指定参数,脚本将对<STDIN>
的每一行执行print
。
然而,如果定义了$ARGV[0]
,我想从那里读取行。这就是第二个有意义的行所要做的。然而,当使用参数运行时,似乎没有行被处理。
我注意到,在条件调用open
之后,$in
并没有改变,即使我希望它改变;
my $in = \*STDIN;
print $in, "\n";
open $in, '<', $ARGV[0] or die if (defined $ARGV[0]);
print $in, "\n";
收益率
GLOB(0xaa08b2f4f28)
GLOB(0xaa08b2f4f28)
即使定义了$ARGV[0]
。当传递的第一个变量已经引用了文件句柄时,open
是否不起作用?
相关文档包括以下内容
关于文件句柄
要打开的第一个参数(在本参考中标记为FILEHANDLE)通常是标量变量。如果对open的调用成功,则作为FILEHANDLE提供的表达式将被分配一个open文件句柄。该文件句柄提供对指定外部文件的内部引用,方便地存储在Perl变量中,并且准备好进行诸如阅读和写的I/O操作。
仅基于这一点,我不明白为什么我的代码不能工作。
2条答案
按热度按时间fhity93d1#
这正是空文件句柄〈〉的作用
<>
的输入来自标准输入,或来自命令行上列出的每个文件。所以你只需要
(see其余的关于它的医生说)
另一种在某些情况下更安全的选择是使用双菱形括号
在while中使用双尖括号会导致open使用三个参数的形式(第二个参数是
<
),因此ARGV
中的所有参数都被视为文本文件名(包括“-”)。(请注意,为了方便起见,如果使用<<>>
,并且@ARGV
为空,它仍然会从标准输入中读取。)(再次,请参阅文档的其余部分)
对于问题的第二部分,以及在评论中的讨论之后,值得注意的是
my $in = \*STDIN
创建了STDIN
的 * 别名 *(不是副本);参见this post。然后打开一个标量为filehandle的文件(之前已经被分配了一个typeglob的引用),仅仅是重定向了原来的typeglob。所以这里,一旦我们open
了$in
文件句柄,那么STDIN
就连接到了那个文件。这很容易检查
在我们输入一些输入后,它会被打印出来,然后按Ctrl-D,在
open
-ing文件后,文件句柄仍然显示为STDIN
,但它确实打印出了这个文件。然后打印STDIN
仍然打印出了这个文件。open
已将STDIN
重新连接到文件;把它找回来并不简单。所以如果你真的想把STDIN
和一个词法关联起来,那么最好复制它。见文档和链接的帖子。至于直接的问题--是的,可以通过
open
-ing来重新分配文件句柄。但是
... or die if ...
语法是错误的,因为不能像那样链接条件句。但是,我无法再现所显示的行为,因为您的代码实际上对我有效(在Linux上的5.16和5.30上)。我最好的猜测是,这样的代码会导致“未定义的行为”,我们会得到不可预测和不一致的行为。
考虑
其中,
E
s代表表达式。(这适用于open(...) or die($!) if COND;
)if E3
应该应用于什么--单独的E2
还是整个E1 or E2
?没有办法告诉我们,人们很可能会得到可怕的“未定义行为”(UB)--它可能实际上工作,有时/在某些条件下/在某些系统上,或者 * 任何 * 其他可能发生的事情。现在,可能还有一点:
E2 if E3
* 不能 * 是条件的一部分,因此将其全部解释为E1 or (E2 if E3);
是直接非法的语法,因此在我的程序中,该语句可能被解释为这很好(并且按预期工作,正如它发生的那样)。然而,原始语句仍然必须是UB,并且在OP的系统上它不工作。
因此,如果您确实需要一个文件句柄,* 至少 * 可以通过添加括号来解决
但是我建议编写一个漂亮的、可读的测试,而不是把它塞进一个语句中(从
STDIN
开始)。1dkrff032#
您希望使用神奇的
ARGV
文件句柄,它可以完全满足您的需要。下面是最安全的阅读方式:
你想做的是这样的:
但是,与unix工具不同的是,这只从提供的第一个文件读取。使用第一个解决方案从提供的每个文件读取。