文件指针中的值不正确

now*_*wox 3 perl

这个简短的例子说明了我在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>);
}
Run Code Online (Sandbox Code Playgroud)

我得到的输出是:

$ ./curious.pl
Processing file 'b' (fp=*main::STDIN)
b file
Processing file 'a' (fp=*main::STDIN)
Processing file 'c' (fp=*main::STDIN)
c file
Run Code Online (Sandbox Code Playgroud)

应该是:

$ ./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
Run Code Online (Sandbox Code Playgroud)

我可能错过了两件事:

  • 为什么$fp等于*main::STDIN而不是当前打开的文件?
  • 为什么'a'不读取内容?

逻辑上,$fp是子程序的本地.它首先被分配,*STDIN然后open用文件指针改变a.然后我处理b.当我返回处理时,b我仍然应该有一个指向a内部的指针$fp.

我在这里读到传递给它的处理程序open必须是一个未定义的标量.然而,它似乎与bc.

Sob*_*que 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";
}
Run Code Online (Sandbox Code Playgroud)

这给出了输出:

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
Run Code Online (Sandbox Code Playgroud)

如果你这样做,只需将那一行改回:

 my $fp = *STDIN;
Run Code Online (Sandbox Code Playgroud)

并且您获得Dumper报告(其余输出为简洁起见):

Process before open a: $VAR1 = *::STDIN
Process  after open a: $VAR1 = *::STDIN;
Run Code Online (Sandbox Code Playgroud)

然而它显然开放的,因为它打印文件内容.

如果你启动strace并运行这两个过程(因此减少):

#!/usr/bin/env perl

my $fh;
open ( $fh, "<", "fishfile" ) or warn $!;
print <$fh>;
Run Code Online (Sandbox Code Playgroud)

运行这个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
Run Code Online (Sandbox Code Playgroud)

随着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
Run Code Online (Sandbox Code Playgroud)

(注意 - 返回代码open是文件描述符号 - 例如3)

那么它实际上做的是:

  • 打开你的新文件
  • 在文件描述符零(即STDIN)上复制它
  • 阅读新的STDIN.
  • 把它写到文件描述符1STDOUT.(2STDERR).

所以结果 - 因为你是 - 通过这样做 - 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";
}
Run Code Online (Sandbox Code Playgroud)

这成功了.我假设根据您之前的问题,这与处理文件,然后根据内容打开子流程有关.

因此,解决方案将测试$name克隆之前是否存在,STDIN您将不会遇到问题.

  • 这是一个很好的答案.我不会想到使用`strace`来看看究竟发生了什么.你刚刚教我一种方法,对许多情况都非常有用.谢谢. (2认同)