Ответ 1
Я уже более 15 лет взламываю Perl, и я признаю, что это предупреждение заставило меня почесать голову на минуту, потому что почти каждый пример вызывает open
в стандартной документации Perl и почти каждый учебник Perl содержит open
без круглых скобок, так же, как вы его написали.
Вы писали этот вопрос в свой первый день с Perl, но вы уже разрешаете strict
и warnings
pragmata! Это отличный старт.
Ложное начало
Легкий, но тупой способ "исправить" предупреждение - отключить все предупреждения. Это был бы ужасный шаг! Предупреждения призваны помочь вам.
Наивные способы шумоподавления предупреждения - отказаться от лексического дескриптора файла в пользу плохого старого способа с помощью голого слова
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
как литеральное имя файла, важное, например, при работе с ненадежный пользовательский ввод -use
open my $fh, "<", $file or die "$0: open $file: $!";
Да, оба заткнут предупреждение, но гораздо более важная выгода заключается в том, что ваша программа обрабатывает неизбежные ошибки, а не игнорирует их и в любом случае заряжает.
Читайте дальше, чтобы понять, почему вы получили предупреждение, полезные подсказки о своей следующей программе Perl, немного философии Perl и рекомендовали улучшения вашего кода. Наконец, вы увидите, что ваша программа не требует явного вызова open
!
Написать полезные сообщения об ошибках
Обратите внимание на важные компоненты сообщения об ошибке, переданного die
:
- программа, которая жаловалась (
$0
) - что он пытался сделать (
"open $file"
) - почему это не удалось (
$!
)
Эти специальные переменные документируются в 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 = @_;
когда вы имели в виду
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 } }
Использование нулевой дескриптор файла (также известный как оператор алмаза) заставляет ваш код вести себя как утилита 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, и разумное использование помогает подчеркнуть то, что, а не как механизм внедрения.
Я надеюсь, что эти предложения помогут!