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

zjk*_*zjk 15 perl warnings suppress-warnings

这是我第一天来到perl,我发现这个警告非常令人困惑.

在./grep.pl第10行的"我的"列表周围缺少括号.

它似乎

open FILE, $file;
Run Code Online (Sandbox Code Playgroud)

工作良好.

出什么问题了

open my $fh, $file;
Run Code Online (Sandbox Code Playgroud)

谢谢!

#!/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;
Run Code Online (Sandbox Code Playgroud)

Gre*_*con 31

我一直在攻击Perl超过15年,我承认这个警告让我头疼了一分钟,因为几乎所有的例子都open在标准的Perl文档中调用,几乎每个Perl教程都包含open没有括号,只是就像你写的那样.

你对你的Perl的第一天写了这个问题,但你已经启用strictwarningspragmata!这是一个很好的开始.

虚假开始

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

扼杀警告的天真方法是放弃词汇文件句柄,转而使用裸字的旧方法

open FH, $file;
Run Code Online (Sandbox Code Playgroud)

使用显式括号 open

open(my $fh, $file);
Run Code Online (Sandbox Code Playgroud)

使my括号明确

open my($fh), $file;
Run Code Online (Sandbox Code Playgroud)

使用限定括号

(open my $fh, $file);
Run Code Online (Sandbox Code Playgroud)

或使用3参数open.

open my $fh, "<", $file;
Run Code Online (Sandbox Code Playgroud)

我建议不要自己使用这些中的任何一个,因为它们都有一个共同的严重遗漏.

最好的方法

一般来说,沉默这个关于缺少括号的警告的最好方法是不添加括号!

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

open my $fh, $file or die "$0: open $file: $!";
Run Code Online (Sandbox Code Playgroud)

要禁用Perl的魔法打开并将其视为$file文件重要的文字名称,例如,在处理不受信任的用户输入时 - 使用

open my $fh, "<", $file or die "$0: open $file: $!";
Run Code Online (Sandbox Code Playgroud)

是的,两者都关闭了警告,但更重要的好处是你的程序处理不可避免的错误而不是忽略它们并且无论如何都要提前充电.

继续阅读以了解为什么您收到警告,有关您下一个Perl程序的有用提示,一些Perl理念以及对您的代码的建议改进.最后,您将看到您的程序不需要显式调用open!

写有用的错误消息

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

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

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

总是检查是否open成功!

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

$ ./mygrep pattern no-such-file
Parentheses missing around "my" list at ./mygrep line 10.
readline() on closed filehandle $fh at ./mygrep line 11.

解释Perl的警告

Perl的警告在perldiag文档中有进一步的解释,启用诊断编译指示将查找perl发出的任何警告的解释.使用您的代码,输出是

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

my $foo, $bar = @_;
Run Code Online (Sandbox Code Playgroud)

当你的意思

my ($foo, $bar) = @_;
Run Code Online (Sandbox Code Playgroud)

请记住my,our,localstate结合非逗号更紧.

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

-Mdiagnostics命令行选项是等效于use diagnostics;在代码,但运行它如上述暂时使诊断的解释,而不必修改您的代码本身.

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

令人费解的是你完全看到警告#1!这是我第一次回忆起与呼叫相关的故事open.5.10.1文档有52个open涉及词法文件句柄的示例用法,但只有两个带有括号my.

它得到了curiouser和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.

缺少括号,所以警告在哪里?!

但是,添加一个小分号警告缺少括号:

$ 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.

让我们看一下perl的来源,看看警告的来源.

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

Perl_localize在op.c哪位处理my,our,state,和local-包含下面的代码片段:

/* 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");
}
Run Code Online (Sandbox Code Playgroud)

注意第一行的注释.在" 我的垃圾邮件生活"中,马克·多米努斯写道:"当然,这是一种启发式方法,这种方式可以说它不起作用."在这种情况下,启发式方法也不起作用,并产生令人困惑的警告.

有条件的

if (sigil && (*s == ';' || *s == '=')) {
Run Code Online (Sandbox Code Playgroud)

解释了为什么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 EOF
Execution of -e aborted due to compilation errors.

我们收到警告!3参数的open情况不会发出警告,因为"<"防止sigil变为真,并且or die ...修饰符以钝的方式通过集合,因为or令牌以;或以外的字符开头=.

警告的目的似乎是提供了一个有用的提示,说明如何修复代码,否则会产生令人惊讶的结果,例如,

$ 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

在这里,警告确实有意义,但你发现的情况是启发式漏洞.

少即是多

Perl具有语法糖,使得编写Unix风格的过滤器变得容易,如perlop文档中所述.

null文件句柄<>很特殊:它可以用来模拟sed和awk的行为.输入来自<>标准输入,或来自命令行中列出的每个文件.以下是它的工作原理:第一次<>计算,@ARGV数组被检查,如果是空,$ARGV[0]则设置为"-",打开时为您提供标准输入.@ARGV然后将该数组作为文件名列表处理.循环

while (<>) {
  ... # code for each line
}
Run Code Online (Sandbox Code Playgroud)

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

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

使用null文件句柄(也称为菱形运算符)使您的代码行为类似于Unix grep实用程序.

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

钻石操作员还处理至少一个您的代码没有的角落案例.请注意,输入中存在条形但不显示在输出中.

$ cat 0
foo
bar
baz
$ ./mygrep bar 0
Parentheses missing around "my" list at ./mygrep line 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/;
}
Run Code Online (Sandbox Code Playgroud)

而不是硬编码路径perl,使用env尊重用户的路径.

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

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

$ ./mygrep ?foo
./mygrep: bad pattern (?foo):
Quantifier follows nothing in regex; marked by <-- HERE in
m/? <-- HERE foo/ at ./mygrep line 10.

主循环既惯用又紧凑.该$_特殊变量是许多Perl的运营商的默认参数,并明智地使用有助于强调什么,而不是实现机械的方式.

我希望这些建议有所帮助!


Dan*_*mer 16

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

my $var1, $var2, $var3;
Run Code Online (Sandbox Code Playgroud)

宣布所有这些.警告应建议您使用正确的表格:

my ($var1, $var2, $var3);
Run Code Online (Sandbox Code Playgroud)

在你的例子中,代码完全符合你的要求(你没有得到任何错误或错误的结果,是吗?),但为了使它绝对清楚你可以写

open my ($fh), $file;
Run Code Online (Sandbox Code Playgroud)

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

my $fh;
open $fh, $file;
Run Code Online (Sandbox Code Playgroud)