这个简短的例子说明了我在Perl中遇到的一个问题。我们的想法是将stdin
作为默认值处理或使用输入文件(如果已指定)。
#!/usr/bin/env perl
qx{echo "a file" > a};
qx{echo "b file" > b};
qx{echo "c file" > c};
process('a');
process('c');
sub process {
my $name = shift;
my $fp = *STDIN;
open $fp, '<', $name if $name;
process('b') if $name eq 'a';
print "Processing file '$name' (fp=$fp)\n";
print while(<$fp>);
}
我得到的输出是:
$ ./curious.pl
Processing file 'b' (fp=*main::STDIN)
b file
Processing file 'a' (fp=*main::STDIN)
Processing file 'c' (fp=*main::STDIN)
c file
应该是:
$ ./curious.pl
Processing file 'b' (fp=*main::STDIN)
b file
Processing file 'a' (fp=*main::STDIN)
a file
Processing file 'c' (fp=*main::STDIN)
c file
我可能错过了两件事:
$fp
等于*main::STDIN
而不是当前打开的文件?'a'
?逻辑上,$fp
是子例程的本地。它首先分配给*STDIN
,然后由open
更改为文件指针a
。然后我处理b
。当我返回到b
的处理时,我仍然应该在a
内指向$fp
。
我已经阅读here传递给open
的处理程序必须是未定义的标量。但是,它似乎适用于b
和c
。
答案 0 :(得分:5)
这与重新分配STDIN
:
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
qx{echo "a file" > a};
qx{echo "b file" > b};
qx{echo "c file" > c};
process('a');
process('c');
sub process {
my $name = shift;
print "Starting process with $name from ", scalar caller(), " \n";
my $fp; # = *STDIN;
print "Process before open $name: ", Dumper($fp), "\n";
open $fp, '<', $name if $name;
print "Process after open $name: ", Dumper($fp), "\n";
process('b') if $name eq 'a';
print "Processing file '$name' (fp=$fp)\n";
print "Contents of $name:\n";
print while (<$fp>);
print "Done with $name\n\n\n";
}
这给出了输出:
Starting process with a from main
Process before open a: $VAR1 = undef;
Process after open a: $VAR1 = \*{'::$fp'};
Starting process with b from main
Process before open b: $VAR1 = undef;
Process after open b: $VAR1 = \*{'::$fp'};
Processing file 'b' (fp=GLOB(0x136412c))
Contents of b:
"b file"
Done with b
Processing file 'a' (fp=GLOB(0x606f54))
Contents of a:
"a file"
Done with a
Starting process with c from main
Process before open c: $VAR1 = undef;
Process after open c: $VAR1 = \*{'::$fp'};
Processing file 'c' (fp=GLOB(0x136412c))
Contents of c:
"c file"
Done with c
如果你这样做,只需将那一行改回:
my $fp = *STDIN;
你得到Dumper报告(为了简洁起见,其余的输出被剪断):
Process before open a: $VAR1 = *::STDIN
Process after open a: $VAR1 = *::STDIN;
然而,显然 正在打开,因为它正在打印文件内容。
如果您启动strace
并执行两个过程(因此减少):
#!/usr/bin/env perl
my $fh;
open ( $fh, "<", "fishfile" ) or warn $!;
print <$fh>;
运行此strace myscript
。 (注意 - strace
是一个特定于Linux的工具 - 其他平台还有其他工具)
(注意 - 我正在使用一个名为fishfile
的文件,其内容为fish
,因为这样我很确定我可以找到该文字:))
执行两次 - 一旦分配STDIN
,您将看到围绕open
操作的一些差异。通过diff
运行它们,你会看到很多,但有趣的部分是:
没有STDIN
作业:
open ( "fishfile", O_RDONLY) = 3
read (3, "fish\n", 8192 ) = 5
write ( 1, "fish\n", 5 ) = 5
使用STDIN
作业:
open ( "fishfile", O_RDONLY) = 3
dup2 ( 3, 0 ) = 0
close ( 3 ) = 0
read (0, "fish\n", 8192 ) = 5
write ( 1, "fish\n", 5 ) = 5
(注意 - open
的返回码是文件描述符号 - 例如3)
那么实际做的是:
STDIN
)STDIN
。 1
或STDOUT
。 (2
是STDERR
)。 结果 - 因为你是 - 通过这样做 - 用你自己的文件描述符来破坏STDIN
,并且因为STDIN
是全局范围的(而不是你的$fh
是词法的作用域):
STDIN
中屏蔽了b
然后将其读取到EOF,这意味着当a
开始阅读它时,那里什么也没有。 但是,如果您在调用open
之后将b
移至:
sub process {
my $name = shift;
print "Starting process with $name from ", scalar caller(), " \n";
my $fp = *STDIN;
process('b') if $name eq 'a';
print "Process before open $name: ", Dumper($fp), "\n";
open $fp, '<', $name if $name;
print "Process after open $name: ", Dumper($fp), "\n";
print "Processing file '$name' (fp=$fp)\n";
print "Contents of $name:\n";
print while (<$fp>);
print "Done with $name\n\n\n";
}
成功运作。我假设根据您之前的问题,这与处理文件,然后根据内容打开子流程有关。
因此,解决方案将在克隆$name
之前测试STDIN
是否存在,您将不会遇到问题。
答案 1 :(得分:3)
my $fp = *STDIN;
open $fp, '<', $name if $name;
与
相同my $fp = *STDIN;
open STDIN, '<', $name if $name;
您正在指示open
更改STDIN
。如果要创建新句柄,则需要将未定义的标量传递给open
。
use strict;
use warnings;
qx{echo "a file" > a};
qx{echo "b file" > b};
qx{echo "c file" > c};
process('a');
process('c');
process('');
sub process {
my $name = shift;
my $fp;
if( defined( $name ) and length( $name ) ) {
open $fp, '<', $name
}else{
$fp = *STDIN;
}
process('b') if defined($name) and $name eq 'a';
print "Processing file '$name' (fp=$fp)\n";
print while(<$fp>);
}
使用echo stdin | perl scriptname
进行测试。
警告从if length($name)
切换到if $name
以避免&#34;行为不端&#34;使用名为0
的文件。