gpt4 book ai didi

perl - 为什么 perl 警告打开我的 $fh, $file 缺少括号?

转载 作者:行者123 更新时间:2023-12-02 06:19:42 34 4
gpt4 key购买 nike

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

Parentheses missing around "my" list at ./grep.pl line 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;

最佳答案

我使用 Perl 已经超过 15 年了,我承认这个警告让我摸不着头脑,因为几乎每个例子都调用 open。在标准 Perl 文档和几乎所有现有的 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 的错误消息的重要组成部分:
  • 提示的程序 ( $0 )
  • 它试图做什么 ( "open $file" )
  • 为什么失败 ( $! )

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

    经常检查是否 open成功!

    再次,始终检查是否 open和其他系统调用成功!否则,您最终会遇到奇怪的错误:
    $ ./mygrep pattern no-such-fileParentheses missing around "my" list at ./mygrep line 10.readline() on closed filehandle $fh at ./mygrep line 11.

    Explanation of Perl's warnings

    Perl's warnings have further explanation in the perldiag documentation, and enabling the diagnostics pragma will look up explanations of any warning that perl emits. With your code, the output is

    $ perl -Mdiagnostics ./mygrep pattern no-such-file
    Parentheses missing around "my" list at ./mygrep line 10 (#1)
    (W parenthesis) You said something like

    my $foo, $bar = @_;

    when you meant

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

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

    readline() on closed filehandle $fh at ./mygrep line 11 (#2)
    (W closed) The filehandle you're reading from got itself closed sometime before now. Check your control flow.

    The -Mdiagnostics command-line option is equivalent to use diagnostics; in your code, but running it as above temporarily enables diagnostic explanations without having to modify your code itself.

    Warning #2 is because no-such-file does not exist, but your code unconditionally reads from $fh.

    It's puzzling that you see warning #1 at all! This is the first time I recall ever seeing it in association with a call to open. The 5.10.1 documentation has 52 example uses of open involving lexical filehandles, but only two of them have parentheses with my.

    It gets curiouser and curiouser:

    $ perl -we 'open my $fh, $file'Name "main::file" used only once: possible typo at -e line 1.Use of uninitialized value $file in open at -e line 1.

    Parentheses are missing, so where's the warning?!

    Adding one little semicolon, however, does warn about missing parentheses:

    $ perl -we 'open my $fh, $file;'Parentheses missing around "my" list at -e line 1.Name "main::file" used only once: possible typo at -e line 1.Use of uninitialized value $file in open at -e line 1.

    Let's look in perl's source to see where the warning comes from.

    $ grep -rl 'Parentheses missing' ../t/lib/warnings/op./op.c./pod/perl561delta.pod./pod/perldiag.pod./pod/perl56delta.pod

    Perl_localize in op.c—which handles my, our, state, and local—contains the following snippet:

    /* 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 'open my $fh, $file ='Parentheses missing around "my" list at -e line 1.syntax error at -e line 1, at EOFExecution of -e aborted due to compilation errors.

    We get the warning! The 3-argument open case doesn't warn because "<" prevents sigil from becoming true, and the or die ... modifier passes muster, in obtuse terms, because the or token begins with a character other than ; or =.

    The intent of the warning appears to be providing a helpful hint for how to fix code that will otherwise produce surprising results, e.g.,

    $ perl -lwe 'my $foo, $bar = qw/ baz quux /; print $foo, $bar'Parentheses missing around "my" list at -e line 1.Useless use of a constant in void context at -e line 1.Use of uninitialized value $foo in print at -e line 1.quux

    Here, the warning does make sense, but the case you found is a leak in the heuristic.

    Less is more

    Perl has syntactic sugar that makes writing Unix-style filters easy, as explained in the perlop documentation.

    The null filehandle <> is special: it can be used to emulate the behavior of sed and awk. Input from <> comes either from standard input, or from each file listed on the command line. Here's how it works: the first time <> is evaluated, the @ARGV array is checked, and if it is empty, $ARGV[0] is set to "-", which when opened gives you standard input. The @ARGV array is then processed as a list of filenames. The loop

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

    is equivalent to the following Perl-like pseudo code:

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

    Using the null filehandle (also known as the diamond operator) makes your code behave like the Unix grep utility.

    • filter each line of each file named on the command line, or
    • filter each line of the standard input when given only a pattern

    The diamond operator also handles at least one corner case that your code doesn't. Note below that bar is present in the input but doesn't appear in the output.

    $ cat 0foobarbaz$ ./mygrep bar 0Parentheses missing around "my" list at ./mygrep line 10.

    Keep reading to see how the diamond operator improves readability, economy of expression, and correctness!

    Recommended improvements to your code

    #! /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尊重用户的路径。

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

    因为你的模式存在于一个变量中,它可能会改变。这并不深奥,但这意味着每次您的代码评估 /$pattern/ 时可能需要重新编译模式。 ,即对于每一行输入。使用 qr// 避免了这种浪费,还提供了一个机会来检查用户在命令行上提供的模式是否是有效的正则表达式。

    $ ./mygrep ?foo
    ./mygrep: 错误模式 (?foo):
    量词在正则表达式中不跟任何东西;标记为 <-- HERE in
    米/? <-- 这里 foo/在 ./mygrep 第 10 行。

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

    我希望这些建议有帮助!

    关于perl - 为什么 perl 警告打开我的 $fh, $file 缺少括号?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5596874/

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