Я взламываю Perl уже более 15 лет, и я признаю, что это предупреждение заставило меня на минуту почесать голову, потому что почти каждый пример вызова open
в стандартной документации Perl и почти каждого Perl Учебное пособие существует open
без скобок, как вы написали.
Вы написали этот вопрос в первый день работы с Perl, но вы уже включили прагматы strict
и warnings
! Это отличное начало.
Ложные пуски
Простой, но тупой способ «исправить» предупреждение - отключить все предупреждения. Это был бы ужасный шаг! Предупреждения призваны помочь вам.
Наивные способы подавить предупреждение - отказаться от лексического дескриптора файла в пользу старого плохого способа с голым словом
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 и трактовать $file
как буквальное имя файла - это важно, например, при работе с ненадежным пользовательским вводом - использование
open my $fh, "<", $file or die "$0: open $file: $!";
Да, оба закрывают предупреждение, но гораздо более важное преимущество в том, что ваша программа обрабатывает неизбежные ошибки, а не игнорирует их и в любом случае заряжается вперед.
Читайте дальше, чтобы понять, почему вы получили предупреждение, полезные советы о вашей следующей программе Perl, немного философии Perl и рекомендуемые улучшения в вашем коде. Наконец, вы увидите, что ваша программа не требует явного вызова open
!
Написать полезные сообщения об ошибках
Обратите внимание на важные компоненты сообщения об ошибке, переданного на die
:
- программа, которая жаловалась (
$0
)
- что он пытался сделать (
"open $file"
)
- почему это не удалось (
$!
)
Эти специальные переменные описаны в perlvar . Выработайте привычку включать эти важные биты в каждое сообщение об ошибке, которое вы увидите, хотя не обязательно те, которые увидит users . Наличие всей этой важной информации сэкономит время отладки в будущем.
Всегда проверяйте, успешно ли open
! 1095 *
Еще раз, всегда проверьте, успешно ли 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 , и включение прагмы Diagnostics будет искать объяснения любого предупреждения, которое выдает Perl. С вашим кодом, вывод
$ perl -Mdiagnostics ./mygrep pattern no-such-file
Круглые скобки отсутствуют в "моем" списке в ./mygrep
строка 10 (# 1)
(В скобках) Вы сказали что-то вроде
my $foo, $bar = @_;
когда вы имели в виду
my ($foo, $bar) = @_;
Помните, что my
, our
, local
и state
связываются сильнее, чем запятая.
readline () на закрытой файловой ручке $fh
в ./mygrep
строка 11 (# 2)
(W закрыто) Файловый дескриптор, с которого вы читаете, закрывался когда-то раньше. Проверьте поток управления.
Параметр командной строки -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
в 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");
}
Обратите внимание на комментарий в первой строке.В My Life With Spam Марк Доминус писал: «Конечно, это эвристика, причудливый способ сказать, что она не работает». Эвристика в этом случае тоже не работаети выдает сбивающее с толку предупреждение.
Условное
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.
Мы получаем предупреждение!Случай с 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 .
Нулевой дескриптор файла <>
является специальным: его можно использовать дляподражать поведению 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
}
}
Использование нулевого дескриптора файла (также известного как оператор diamond) заставляет ваш код вести себянапример, утилита 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/;
}
Вместо жесткого кодирования пути к 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.
ThОсновной цикл идиоматичен и компактен.Специальная переменная $_
является аргументом по умолчанию для многих операторов Perl, и разумное использование помогает подчеркнуть что , а не как механизм реализации.
Я надеюсь, что эти предложения помогут!