a};-6ren">
gpt4 book ai didi

perl - 文件指针中的错误值

转载 作者:行者123 更新时间:2023-12-05 00:33:35 26 4
gpt4 key购买 nike

这个简短的例子说明了我在 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 的处理时,我应该仍然在 $fp 中有一个指向 a 的指针。

我已阅读 here传递给 open 的处理程序必须是未定义的标量。但是它似乎适用于 bc

最佳答案

这与重新分配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
  • 将其写入文件描述符1STDOUT。 (2STDERR)。

因此 - 因为你是 - 通过这样做 - 用你自己的文件描述符破坏 STDIN,并且因为 STDIN 是全局范围的(而不是你的 $fh 是词法范围的):

  • 您在子进程 b 中屏蔽 STDIN 然后读取它直到 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";
}

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

因此,解决方案是在克隆 STDIN 之前测试 $name 的存在,这样您就不会遇到问题。

关于perl - 文件指针中的错误值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31110393/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com