【问题标题】:Perl eating line one on -n commandline option flagPerl 在 -n 命令行选项标志上吃第一行
【发布时间】:2015-12-07 13:40:43
【问题描述】:

我已经开始使用 perl,我试图弄清楚告诉 perl 如果我也提供循环使用循环有什么问题?

看起来 perl 对相同的打开文件描述符感到困惑,但我不明白为什么它会吃掉第一行?

perl -ne 'while (<>) { print $_; }'

当然,在这个简单的例子中,我可以简单地perl -ne '{print $_}' 来达到相同的功能逻辑。

但我想知道的是,如果另一个while (<>) { } 被包装,第一行消失的双循环出了什么问题?

$ perl -ne '{print $_}' hello
hello
hello
world
world
^C

$ perl -ne 'while (<>) { print $_; }' 
hello
world
world
^C

更新:根据答案,似乎正在发生的是 Perl 正在等待 STDIN 输入的第一个循环。在接收到 STDIN 上的输入后,该输入被分配给内部缓冲区 $_,并且逻辑继续进行第二个循环,在该循环中它再次等待新的 STDIN 输入。收到新的 STDIN 输入后,它会使用新的 STDIN 输入破坏 STDIN 缓冲区$_ 并开始打印。

【问题讨论】:

  • 如果您不想跳过第一行,您应该在第二个 while 循环之前 print()。 perl -ne 'print; while (&lt;&gt;) { print }'
  • @Сухой27 谢谢这是解决问题的方法,但我试图弄清楚为什么有必要这样做。通过打开第二个调用,行缓冲区被吃掉了。为什么没有为 STDIN 文件描述符输出缓冲的所有数据?我尝试了select 0; $|=1; select 1; $|=1 之类的东西,但结果是一样的。
  • '{print $_}' 不正确。它应该只是print $_(或者甚至只是print)。花括号 { } 在 Perl 中创建一个哈希引用。这不像在awk中。另外,如果你想打印这些行,你应该改用-p,就像-n,除了它也会打印。

标签: perl


【解决方案1】:

您可以使用O=Deparse自行检查one-liner生成的代码。

第一:

$ perl -MO=Deparse  -ne 'print $_;' file
LINE: while (defined($_ = <ARGV>)) {
    print $_;
}
-e syntax OK

第二:

$ perl -MO=Deparse -ne 'while (<>) { print $_; }' file
LINE: while (defined($_ = <ARGV>)) {
    while (defined($_ = <ARGV>)) {
        print $_;
    }
}
-e syntax OK

现在,很容易知道第二种情况出了什么问题。外层 while 吃掉文件的第一行就丢失了。

【讨论】:

  • 感谢它的有用。我知道正在生成第二个循环,但这提供了更多的洞察力,但仍然不能解释为什么第一行消失了。在我看来,Perl 不会因为内部缓冲而刷新,并且可能是由于错误,在这种情况下它只是转储缓冲的数据而不是刷新缓冲区。
  • @A.Danischewski,请参阅第二个代码 sn-p,在执行 $_ = &lt;ARGV&gt; 时先在外部,这意味着将 $_ 分配给文件的第一行并且不打印它,您正在再次执行$_ = &lt;ARGV&gt; 现在您正在打印该行,因此 $_ 的先前值被覆盖或丢失(在这种情况下)。
  • @A.Danischewski $foo = 1; $foo = 2; print $foo 1?
  • 这不是perl“更聪明一点”的问题 - 这样做是因为您专门要求它通过设置-n
  • @A.Danischewski 我想你很困惑。 -n 开关非常明智,只是你用错了。 perl -ne'print $_' 会做你所期望的。如果您添加更多代码,您可以更改默认行为,这就是您所做的:您添加了另一个 readline 循环。哎呀,如果你输入perl -ne'print "NO, I'm not printing any lines!"',它根本不会从输入中打印任何行。如果您输入perl -ne'die "I'm a little teapot!"',它也不会打印任何内容。您是在告诉 perl 通过添加该代码来丢弃第一行。
【解决方案2】:

-n 标志将您的代码包装在 while (&lt;&gt;) { ... } 构造中。

所以在你的第二个例子中,实际执行的代码是

while (<>) # reads a line from STDIN, places it in $_
{
    # you don't do anything with the contents of $_ here

    while (<>) # reads a line from STDIN, places it in $_, overwriting the previous value
    {
        print $_; # prints the contents of $_
    }
}

这意味着第一个 &lt;&gt; 读取的行刚刚丢失。

【讨论】:

  • 刚刚迷路了?应该以什么理由丢失?为什么不冲?此外,这没有任何意义,因为我在执行第一个缓冲区 STDIN 时没有数据,程序在开始接收数据时设置为第二个 while 循环——但仍然转储第 1 行?
  • &lt;&gt; 读取一行,然后将其放入$_。然后你不用它做任何事情,所以,是的,它被丢弃(被下一个&lt;&gt; 替换,它用下一行覆盖$_)。 除非您特别要求,否则不会打印任何内容(就像您对内部 print $_; 所做的那样)。
  • @A.Danischewski,扩大了一点答案,这样你就可以看到会发生什么。
猜你喜欢
  • 2013-04-14
  • 2020-09-21
  • 2011-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多