【问题标题】:Why does perl warn that open my $fh, $file is missing parentheses?为什么 perl 会警告 open my $fh, $file 缺少括号?
【发布时间】:2011-08-01 14:21:05
【问题描述】:

这是我使用 perl 的第一天,我发现这个警告非常令人困惑。

./grep.pl 第 10 行的“我的”列表周围缺少括号。

好像

open FILE, $file;

工作正常。

有什么问题

open my $fh, $file;

谢谢!

#!/usr/bin/perl

use strict;
use warnings;

sub grep_all {
        my $pattern = shift;

        while (my $file = shift) {
                open my $fh, $file;
                while (my $line = <$fh>) {
                        if ($line =~ m/$pattern/) {
                                print $line;
                        }   
                }   
        }   
}

grep_all @ARGV;

【问题讨论】:

  • 总是,总是总是检查open是否成功!
  • 使用三个参数open

标签: perl warnings suppress-warnings


【解决方案1】:

my 用于声明变量或它们的列表。写Perl是一个常见的错误

my $var1, $var2, $var3;

声明所有这些。警告应该建议您使用正确的形式:

my ($var1, $var2, $var3);

在您的示例中,代码完全符合您的要求(您没有得到任何错误或错误结果,是吗?),但为了清楚起见,您可以编写

open my ($fh), $file;

虽然有人可能会争辩说,将my 放在行的中间就像隐藏它一样。也许更具可读性:

my $fh;
open $fh, $file;

【讨论】:

  • +1 用于将 my 放在自己的行中...这样更容易注意到。
【解决方案2】:

要获得更详细的警告消息解释,请使用perldoc diagnostics。 例如,

use strict;
use warnings;
use diagnostics;

my $fh, $file;

将生成以下有用的解释:

“我的”列表周围缺少括号 (W括号)你说过类似

    my $foo, $bar = @_;

when you meant

    my ($foo, $bar) = @_;

Remember that "my", "our", and "local" bind tighter than comma.

您还可以在命令提示符下查看my 的文档:

perldoc -f my

如果列出了多个值,则 列表必须放在括号中。

【讨论】:

    【解决方案3】:

    我已经 hack Perl 超过 15 年了,我承认这个警告让我一时摸不着头脑,因为在标准 Perl 文档中几乎每个示例都调用 open 并且几乎每个 Perl现有教程包含open,没有括号,就像你写的一样。

    您在使用 Perl 的第一天就写了这个问题,但您已经启用了 strictwarnings 实用程序!这是一个很好的开始。

    假开始

    “修复”警告的一种简单但愚蠢的方法是禁用所有警告。这将是一个可怕的举动!警告旨在帮助您。

    消除警告的幼稚方法是放弃lexical filehandle,转而使用裸词的旧方法

    open FH, $file;
    

    open 中使用显式括号

    open(my $fh, $file);
    

    明确my 的括号

    open my($fh), $file;
    

    使用带括号的括号

    (open my $fh, $file);
    

    或使用 3 参数 open

    open my $fh, "<", $file;
    

    我建议反对单独使用其中任何一种,因为它们都有一个严重的共同点。

    最好的方法

    一般而言,消除有关缺少括号的警告的最佳方法是添加无括号!

    始终检查open 是否成功,例如

    open my $fh, $file or die "$0: open $file: $!";
    

    要禁用 Perl 的 magic open 并将 $file 视为文件的字面名称——这很重要,例如,在处理 untrusted user input 时——使用

    open my $fh, "<", $file or die "$0: open $file: $!";
    

    是的,两者都会关闭警告,但更重要的好处是您的程序可以处理不可避免的错误,而不是忽略它们并继续收费。

    继续阅读以了解您收到警告的原因、关于您的下一个 Perl 程序的有用提示、一些 Perl 理念以及对代码的改进建议。最后,您会看到您的程序不需要显式调用open

    编写有用的错误消息

    注意传递给die的错误消息的重要组成部分:

    1. 投诉的程序 ($0)
    2. 它试图做什么 ("open $file")
    3. 为什么会失败 ($!)

    这些特殊变量记录在perlvar 中。现在养成将这些重要信息包含在您将看到的每条错误消息中的习惯——尽管不一定是用户会看到的那些。掌握所有这些重要信息将节省未来的调试时间。

    始终检查open 是否成功!

    再一次,总是检查open和其他系统调用是否成功!否则,您最终会遇到奇怪的错误:

    $ ./mygrep 模式 no-such-file
    ./mygrep 第 10 行的“我的”列表周围缺少括号。
    在 ./mygrep 第 11 行关闭文件句柄 $fh 上的 readline()。

    Perl 警告说明

    Perl 的警告在perldiag documentation 中有进一步的解释,启用diagnostics pragma 将查找perl 发出的任何警告的解释。使用您的代码,输出是

    $ perl -Mdiagnostics ./mygrep pattern no-such-file
    ./mygrep 第 10 行 (#1) 的“我的”列表周围缺少括号
    (W括号)你说过类似

    my $foo, $bar = @_;
    

    你的意思

    my ($foo, $bar) = @_;
    

    请记住,myourlocalstate 的绑定比逗号更紧密。

    ./mygrep 第 11 行关闭文件句柄 $fh 上的 readline() (#2)
    (W 关闭)您正在读取的文件句柄在现在之前的某个时间自行关闭。检查您的控制流。

    -Mdiagnostics 命令行选项等效于您代码中的use diagnostics;,但按上述方式运行它暂时可以启用诊断说明,而无需修改您的代码本身。

    警告 #2 是因为 no-such-file 不存在,但您的代码无条件从 $fh 读取。

    令人费解的是,您完全看到了警告 #1!这是我记得第一次看到它与对open 的调用有关。 5.10.1 文档有 52 个使用 open 的示例,涉及词法文件句柄,但其中只有 两个 带有带有 my 的括号。

    它变得越来越好奇:

    $ perl -we '打开我的 $fh, $file'
    名称“main::file”仅使用一次:-e 第 1 行可能有错字。
    在 -e 第 1 行打开时使用未初始化的值 $file。

    括号不见了,那么警告在哪里?!

    然而,添加一个小分号,确实警告缺少括号:

    $ perl -we '打开我的 $fh, $file;'
    -e 第 1 行的“我的”列表周围缺少括号。
    名称“main::file”仅使用一次:-e 第 1 行可能有错字。
    在 -e 第 1 行打开时使用未初始化的值 $file。

    让我们看看 perl 的源代码,看看警告来自哪里。

    $ grep -rl '括号丢失' 。
    ./t/lib/警告/操作
    ./op.c
    ./pod/perl561delta.pod
    ./pod/perldiag.pod
    ./pod/perl56delta.pod

    Perl_localize in op.c——处理myourstatelocal——包含以下sn-p:

    /* some heuristics to detect a potential error */
    while (*s && (strchr(", \t\n", *s)))
      s++;
    
    while (1) {
      if (*s && strchr("@$%*", *s) && *++s
           && (isALNUM(*s) || UTF8_IS_CONTINUED(*s))) {
        s++;
        sigil = TRUE;
        while (*s && (isALNUM(*s) || UTF8_IS_CONTINUED(*s)))
          s++;
        while (*s && (strchr(", \t\n", *s)))
          s++;
      }
      else
        break;
    }
    if (sigil && (*s == ';' || *s == '=')) {
      Perl_warner(aTHX_ packWARN(WARN_PARENTHESIS),
        "Parentheses missing around \"%s\" list",
        lex
          ? (PL_parser->in_my == KEY_our
            ? "our"
            : PL_parser->in_my == KEY_state
              ? "state"
              : "my")
          : "local");
    }
    

    注意第一行的注释。在My Life With Spam 中,Mark Dominus 写道:“当然,这是一种启发式方法,这是一种说它不起作用的奇特方式。”在这种情况下,启发式也不起作用,并产生令人困惑的警告。

    条件

    if (sigil && (*s == ';' || *s == '=')) {
    

    解释了为什么perl -we 'open my $fh, $file' 不发出警告,而是使用尾随分号发出警告。观察类似但无意义的代码会发生什么:

    $ perl -we '打开我的 $fh, $file ='
    -e 第 1 行的“我的”列表周围缺少括号。
    -e 第 1 行,EOF 处的语法错误
    -e 的执行由于编译错误而中止。

    我们收到警告! 3 参数open 的情况不会发出警告,因为"&lt;" 阻止sigil 变为真,并且or die ... 修饰符通过了集合,用钝的术语来说,因为or 标记以@ 以外的字符开头987654387@或=

    警告的意图似乎是为如何修复会产生令人惊讶的结果的代码提供有用的提示,例如

    $ perl -lwe '我的 $foo, $bar = qw/ baz quux /;打印 $foo, $bar'
    -e 第 1 行的“我的”列表周围缺少括号。
    在 -e 第 1 行的 void 上下文中无用地使用常量。
    在 -e 第 1 行的 print 中使用未初始化的值 $foo。
    qux

    这里,警告确实有意义,但您发现的情况是启发式泄漏。

    少即是多

    Perl 的语法糖使编写Unix-style filters 变得容易,如perlop 文档中所述。

    空文件句柄&lt;&gt; 很特别:它可以用来模拟sed 和awk 的行为。 &lt;&gt; 的输入要么来自标准输入,要么来自命令行中列出的每个文件。下面是它的工作原理:第一次评估&lt;&gt;,检查@ARGV 数组,如果它为空,则$ARGV[0] 设置为"-",打开时会为您提供标准输入。然后将@ARGV 数组作为文件名列表进行处理。循环

    while (<>) {
      ... # code for each line
    }
    

    等价于以下类似 Perl 的伪代码:

    unshift(@ARGV, '-') unless @ARGV;
    while ($ARGV = shift) {
      open(ARGV, $ARGV);
      while (<ARGV>) {
        ... # code for each line
      }
    }
    

    使用空文件句柄(也称为菱形运算符)使您的代码表现得像 Unix grep 实用程序。

    • 过滤命令行命名的每个文件的每一行,或者
    • 仅在给定模式时过滤标准输入的每一行

    菱形运算符还可以处理您的代码无法处理的至少一种极端情况。请注意下面的条形图出现在输入中,但未出现在输出中。

    $ 猫 0
    富
    酒吧
    巴兹
    $ ./mygrep 栏 0
    ./mygrep 第 10 行的“我的”列表周围缺少括号。

    继续阅读,了解菱形运算符如何提高可读性、表达经济性和正确性!

    建议的代码改进

    #! /usr/bin/env perl
    
    use strict;
    use warnings;
    
    die "Usage: $0 pattern [file ..]\n" unless @ARGV >= 1;
    
    my $pattern = shift;
    
    my $compiled = eval { qr/$pattern/ };
    die "$0: bad pattern ($pattern):\n$@" unless $compiled;
    
    while (<>) {
      print if /$compiled/;
    }
    

    不要硬编码到perl 的路径,而是使用env 来尊重用户的PATH。

    与其盲目假设用户在命令行上至少提供了一个模式,不如检查它是否存在或提供有用的使用指南。

    因为您的模式存在于变量中,所以它可能会改变。这几乎没有什么意义,但这意味着每次您的代码为每一行输入计算/$pattern/时,可能需要重新编译该模式。使用qr// 可以避免这种浪费,还可以检查用户在命令行中提供的模式是否是有效的正则表达式。

    $ ./mygrep ?foo
    ./mygrep: 错误模式 (?foo):
    量词不遵循正则表达式;由 
    
    

    主循环既惯用又紧凑。 $_ 特殊变量是许多 Perl 运算符的默认参数,明智地使用有助于强调 what 而不是实现机制的方式。

    希望这些建议对您有所帮助!

    【讨论】:

    • Greg,我当然总是在我的教程中打开的示例中使用括号。我这样做是因为优先级太难记了。我总是搞砸or// 之类的东西,所以我只使用我真正理解的C 连词。因此我总是使用括号。也更容易阅读。
    • 你能在的解释中解释@ARGV中“-”不移位的原因吗?我是 Perl 初学者。谢谢!
    • @Alby 请注意,它是有条件的,仅在 @ARGV 为空时发生。 Perl 的魔法 open 将 - 视为标准输入的同义词,当命令行上没有参数时,空文件句柄或“菱形运算符”会从中读取。
    • 谢谢。这个解释消除了我最初的困惑“为什么要放一些你会在下一行取出的东西?” :)
    【解决方案4】:

    真正的问题是忽略函数调用是相当脆弱的。如果你这样做,预计会出现奇怪的错误。

    $ perl -we'$file="abc"; open(my $fh, $file);'
    
    $ perl -we'$file="abc"; open my $fh, $file;'
    Parentheses missing around "my" list at -e line 1.
    

    【讨论】:

    • 是的。我强烈建议对所有函数调用使用括号。
    【解决方案5】:

    在我看来,你的代码比它需要的要长——你应该使用更多的懒惰。

    #!/usr/bin/env perl
    my $pattern = shift;
    while (<>)
    {
        print if m/$pattern/;
    }
    

    如果您决定需要行号或文件名(可能有多个文件)或其他一些更复杂的打印,那么您可能需要将内容写出来。但我相信我展示的代码和你展示的代码是等价的。

    通常,我会在代码中添加 use strict;use warnings;。然而,在这个例子中,唯一的命名变量是用my 定义的(所以严格不会有帮助),并且没有什么需要警告的。但是,如果您正在学习 Perl,或者如果程序比这复杂得多,我会添加 use 行,即使在使用 Perl 大约 20 年后也是如此。

    【讨论】:

      【解决方案6】:

      您可能正在做一个学校或学习项目。但是当我想做类似 perl 的事情时,我通常会使用你的程序的这个更简洁的版本。

      perl -ne 'print if /your_regex/' your_file_list

      欲了解更多信息,请尝试

      perldoc perlrun

      并寻找-n和-p的解释。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-03-19
        • 2012-12-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多