Я взламываю 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. Выработайте привычку включать эти важные фрагменты в каждое сообщение об ошибке, которое вы увидите, хотя и не обязательно в те, которые увидят пользователи. Наличие всей этой важной информации сэкономит время на отладку в будущем.
Всегда проверяйте, удалось ли 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
at ./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");
}
Обратите внимание на комментарий к первой строке. В статье Моя жизнь со спамом Марк Доминус пишет: «Конечно, это это эвристика, то есть причудливый способ сказать, что это не работает». Эвристика в этом случае тоже не работает и выдает запутанное предупреждение.
условное
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.
Получаем предупреждение! Случай 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
}
}
Использование пустого дескриптора файла (также известного как алмазный оператор) заставляет ваш код вести себя как утилита grep Unix.
- фильтровать каждую строку каждого файла, указанного в командной строке, или
- фильтровать каждую строку стандартного ввода, когда задан только шаблон
Алмазный оператор также обрабатывает по крайней мере один угловой случай, которого нет в вашем коде. Обратите внимание, что полоса присутствует на входе, но не отображается на выходе.
$ 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.
Основной цикл одновременно идиоматичен и компактен. Специальная переменная $_
является аргументом по умолчанию для многих операторов Perl, и ее разумное использование помогает подчеркнуть что, а не как механизма реализации.
Я надеюсь, что эти предложения помогут!
person
Community
schedule
08.04.2011
open
! - person Greg Bacon   schedule 08.04.2011