2011-04-08 84 views
15

这是我佩戴的第一天,我发现这个警告很混乱。

括号在./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; 
+12

总是,*总是*,**总是**检查'open'是否成功! – 2011-04-08 14:55:47

+7

用三个参数[打开](http://perldoc.perl.org/functions/open.html) – 2011-04-08 15:01:27

回答

31

我一直在黑客的Perl超过15年了,我承认这个警告使我抓我的头一分钟,因为标准的Perl文档中的几乎每一个例子调用open几乎所有的Perl教程存在包含open没有括号,就像你写的。

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

假开始

一个简单而愚蠢的方式来“修复”的警告是禁用所有警告。这将是一个可怕的举措!警告旨在帮助你。

朴素的方式来压制警告赞成的不良旧方式使用明确的括号中的裸词

open FH, $file; 

open

open(my $fh, $file); 

使my的括号明确

放弃 lexical filehandle
open my($fh), $file; 

个使用外接括号

(open my $fh, $file); 

,或者使用三个参数open

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

我建议使用其中任何自己,因为他们都有一个共同的严重疏漏。

最好的办法

一般情况下,沉默失踪括号此警告的最佳方式包括将没有括号!

请务必确认open是否成功,例如

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

要禁用Perl的magic open和治疗$file为一个文件,重要的,例如,与untrusted user input -use

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

是,无论闭嘴警告打交道时的文字名称,但更重要的好处是你的程序处理不可避免的错误,而不是无视它们,并无论如何收费。

看了就明白了,为什么你得到了警告,对你的下一个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 documentation进一步解释,并启用diagnostics pragma将查找是Perl发出任何警告的解释。与您的代码,输出是

$ perl -Mdiagnostics ./mygrep pattern no-such-file
括号缺少围绕 “我” 列表中./mygrep线10(#1)
(W括号)您说的一样

my $foo, $bar = @_; 

当你的意思是

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

请记住, my,our,localstate绑定比逗号更紧密。在./mygrep线11(#2)
(W关闭),你从阅读的文件句柄

的ReadLine()在关闭文件句柄$fh得到了自己之前的某个时候,现在关闭。检查你的控制流程。

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

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

令人费解的是,你看到警告#1!这是第一次我记得曾经一起open呼叫看到它的关联。该5.10.1文档中有52例的open包括词法文件句柄使用,但其中只有有圆括号my

它变得奇妙而又奇妙:

$ 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 in op.c哪位处理myourstate,并且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"); 
} 

公告第一行的注释。在My Life With Spam,马克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 EOF 
Execution of -e aborted due to compilation errors.

我们收到警告!因为"<"阻止sigil成为true,并且or die ...修饰符以钝角条件通过调整,因为or标记以;=以外的其他字符开头,所以3个参数open的情况不会发出警告。

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

$ 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-style filters容易的,因为perlop文档中解释语法糖。

的空文件句柄<>是特殊的:它可以用来模拟的sed和awk行为。从<>输入是无论是从标准输入,或在命令行上列出的每个文件。以下是它的工作原理:第一次对<>进行评估时,将检查@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工具。

  • 滤波器命名的命令行上的每个文件的每一行,或
  • 滤波器只给出一个图案时的标准输入的每一行

金刚石操作者也处理至少一个角的情况下你的代码没有。请注意,输入栏中存在但未出现在输出中。

$ 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):\[email protected]" unless $compiled; 

while (<>) { 
    print if /$compiled/; 
} 

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

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

因为你的模式存在于一个变量中,它可能会改变。这并不深刻,但这意味着每次您的代码评估/$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运算符的默认参数,而明智的使用有助于强调执行机制的方式,而不是实现的机制。

我希望这些建议帮助!

+1

Greg,在我的教程中,我总是在我的开放示例中使用parens。我这样做是因为优先级太难让我记住。我总是被'或'和'//'等东西搞砸,所以我只使用了我真正理解的C连词。因此我总是使用parens。使它更易于阅读。 – tchrist 2011-04-08 19:23:43

+0

你能否在<解释>中解释在@ARGV中不移动“ - ”的原因?我是一名Perl初学者。谢谢! – Alby 2012-04-18 21:13:57

+1

@Alby请注意,这是有条件的,只有在@ARGV为空时才会发生。 Perl的魔术开放式对待 - 作为标准输入的同义词,当命令行中没有参数时,空文件句柄或“菱形操作符”将从中读取。 – 2012-04-18 23:11:49

16

my是用于声明变量或一个他们的列表。在Perl中写入

my $var1, $var2, $var3; 

要声明所有这些是常见的错误。警告应该建议你用正确的形式:(?你没有得到任何错误或错误的结果,你有没有)

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

在您的示例代码不正是你想要的,而是使之绝对清除你可以写

open my ($fh), $file; 

虽然人们可以争辩说,把my在一行的中间就像隐藏它。也许更具可读性:

my $fh; 
open $fh, $file; 
+1

我看看。谢谢 ! – zjk 2011-04-08 15:01:37

+0

+1将我放在自己的路线上......更容易注意到这一点。 – 2011-04-08 16:45:30

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 

如果超过一个值被列出,所述 列表必须放在括号中。

1

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

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

就是这样。我强烈建议对所有函数调用使用parens。 – tchrist 2011-04-08 19:24:53

1

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

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

如果你决定你需要的行号,或文件名(也许如果有一个以上的文件),或者其他一些更复杂的印刷,那么你可能想要写的东西出来。但我相信我展示的代码与您展示的代码相同。

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

0

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

的perl -ne '如果打印/ your_regex /' your_file_list

欲了解更多信息尝试

的perldoc perlrun

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