函数定义更改<>的向外行为

时间:2019-06-16 00:44:50

标签: perl stdin

重要提示:该问题的动机不是解决问题,而是了解Perl的行为。


请考虑以下玩具脚本:

#!/usr/bin/env perl

use strict;

sub main {

  @ARGV >= 2 or die "$0: not enough arguments\n";

  my $arg_a = shift @ARGV;
  my $arg_b = shift @ARGV;

  while ( <> ) {
    print "+++ $_";
  }
}

main();

__END__

此脚本带有2个或更多参数(不使用)。它所做的只是回显(使用+++前缀)标准输入,或者将许多文件的内容指定为第三,第四等参数。

到目前为止,代码的行为符合我的预期。

现在考虑这个经过稍微修改的版本:

#!/usr/bin/env perl

use strict;

sub slurp {
  local $/ = undef;
  local @ARGV = @_;
  return <>;
}

sub main {

  @ARGV >= 2 or die "$0: not enough arguments\n";

  my $arg_a = shift @ARGV;
  my $arg_b = shift @ARGV;

  my $content = slurp( $arg_a );

  while ( <> ) {
    print "+++ $_";
  }
}

main();

__END__

此脚本版本不会忽略其第一个参数;而是将其解释为文件的路径,并将其内容读入变量$content(随后将其忽略)。除此之外,该脚本的行为应与以前完全相同。

不幸的是,此版本的脚本不再回显其stdin(尽管它仍然回显其3rd,4th等参数的内容)。

我知道问题与slurp函数的实现方式有关,因为如果我将此实现更改为

sub slurp {
  local $/ = undef;
  open my $input, $_[ 0 ] or die "$!";
  return <$input>;
}

...然后,脚本再次响应其标准输入(如果可用)。

我想了解为什么slurp的第一个版本导致脚本按预期停止工作。

2 个答案:

答案 0 :(得分:2)

要使<>STDIN一起使用,必须在@ARGV为空时调用它。如果运行@ARGV<>中有文件名,则在读取文件时会将它们从那里删除,然后您需要再次调用<> 以便等待STDIN

perl -wE'if (@ARGV) { print while <> }; print while <>' file

第二个print while <>等待STDIN(不打印file并退出程序)。

如果从@ARGV读取所有文件,并且一旦控件返回到主要的<>调用之后,则子程序原则上会发生这种情况,然后您将等待{{ 1}}。

但是,您的子STDIN本地化(好的做法!),因此,一旦退出全局@ARGV,它仍然具有开始时的功能。然后,主体中的@ARGV再次读取这些文件,在最后一个文件的末尾获得一个应有的while,然后退出。

一种查看方式:在调用读取输入的子程序之后,在主程序undef之前,从@ARGV中删除所有内容。然后,while将再次等待while,而与子无关。喜欢

STDIN

(需要注意的一个细节是,您的示例似乎使用了两个文件,而子文件处理了一个文件,因此即使子文件使用的是全局perl -wE' sub ri { local @ARGV = @_; return <> }; print for ri(@ARGV); say"argv: @ARGV"; @ARGV=(); print while <> ' file (不是@ARGV化的文件),从local中删除一个文件,仍然有一个文件占据了@ARGV的主体。因此您仍然不会得到while。)

查看全部内容的另一种方法:在末尾添加另一个STDIN那个一个人会在print while <>上等待。

这一切都在I/O Operators (perlop)中进行了描述,尽管需要仔细阅读。


STDIN上,local $GLOBAL_VAR;的值被复制掉,并在退出该范围时恢复。因此,local在其范围内保护全局变量不受更改。

答案 1 :(得分:2)

在考虑再次使用STDIN之前,需要耗尽迭代器(通过调用它直到返回undef)。

sub slurp {
   local $/ = undef;
   local @ARGV = @_;
   my $rv = <>;   # Read file specified by $_[0].
   1 while <>;    # Exhaust the iterator.
   return $rv;
}

sub slurp {
   local $/ = undef;
   local @ARGV = @_;
   my $rv = "";
   while (my $file = <>) {
      $rv .= $file;
   }

   return $rv;  # Concatenation of all files specified by @_.
}