He estado hackeando Perl desde hace más de 15 años, y tengo que admitir esta advertencia hizo que me rasco la cabeza durante un minuto, porque casi todos los ejemplos de llamadas a open
en la documentación estándar de Perl y casi cada tutorial de Perl en existencia contiene open
sin paréntesis, tal como lo escribió.
Usted escribió esta pregunta en su primer día con Perl, pero que ya está permitiendo la prágmata strict
y warnings
! Este es un excelente comienzo.
Inicios falsos
Una manera fácil, pero tonto para “fijar” la advertencia es para desactivar todas las advertencias. ¡Esto sería un movimiento terrible! Las advertencias están destinadas a ayudarte.
formas ingenuas para aplastar la advertencia están abandonando el lexical filehandle a favor de la mala manera de edad con una bareword
open FH, $file;
usando paréntesis explícitos con open
open(my $fh, $file);
hacer my
's paréntesis explícitos
open my($fh), $file;
utilizando paréntesis circunscritos
(open my $fh, $file);
o utilizando 3-argumento open
.
open my $fh, "<", $file;
recomiendo contra el uso de cualquiera de éstos por sí mismos porque todos tienen una omisión grave en común.
El mejor enfoque
En general, la mejor manera de silenciar esta advertencia sobre el paréntesis que faltan implica añadir no hay paréntesis!
controle siempre si open
tiene éxito, por ejemplo ,
open my $fh, $file or die "$0: open $file: $!";
Para desactivar Perl de magic open y tratar $file
como el nombre literal de un archivo importante, por ejemplo, cuando se trata de untrusted user input -uso
open my $fh, "<", $file or die "$0: open $file: $!";
Sí, tanto callar la advertencia, pero el beneficio mucho más importante es que su programa maneja errores inevitables en lugar de ignorarlos y cargarlos de todos modos.
Siga leyendo para comprender por qué recibió la advertencia, consejos útiles sobre su próximo programa Perl, un poco de filosofía Perl y las mejoras recomendadas para su código. ¡Finalmente, verá que su programa no requiere una llamada explícita al open
!
mensajes de error de escritura útiles
Aviso los componentes importantes del mensaje de error pasado a die
:
- el programa que se quejó (
$0
)
- lo que trató de hacer (
"open $file"
)
- por qué falló (
$!
)
Estas variables especiales están documentadas en perlvar. Desarrolle el hábito ahora de incluir estos bits importantes en cada mensaje de error que verá, aunque no necesariamente aquellos que usuarios verán. Tener toda esta información importante ahorrará tiempo de depuración en el futuro.
Siempre compruebe si open
tiene éxito.
Una vez más, siempre compruebe si open
y otras llamadas al sistema tienen éxito! De lo contrario, se termina con errores extraños:
$ ./mygrep pattern no-such-file
Parentheses missing around "my" list at ./mygrep line 10.
readline() on closed filehandle $fh at ./mygrep line 11.
Explicación de las advertencias de Perl
advertencias de Perl tiene una explicación más detallada en el perldiag documentation, y permitiendo que el diagnostics pragma buscará explicaciones de cualquier advertencia que perl emite. Con su código, la salida es
$ perl -Mdiagnostics ./mygrep pattern no-such-file
Los paréntesis que faltan en torno a "mi" lista en ./mygrep
línea 10 (# 1)
(W paréntesis) Dijiste algo así como
my $foo, $bar = @_;
cuando se refería a
my ($foo, $bar) = @_;
Recuerde que my
, our
, local
y state
se unen más que comas.
readline() el gestor de archivo cerrado $fh
en ./mygrep
línea 11 (# 2)
(W cerrado) El gestor de archivo que está leyendo desde GOT en sí cerrado en algún momento antes de ahora. Verifica tu flujo de control.
La opción -Mdiagnostics
de línea de comandos es equivalente a use diagnostics;
en su código, pero el funcionamiento es que el anterior temporalmente permite explicaciones de diagnóstico sin tener que modificar el código en sí.
Advertencia n. ° 2 es porque no-such-file
no existe, pero su código se lee incondicionalmente de $fh
.
¡Es desconcertante que vea la advertencia n. ° 1 en absoluto!Esta es la primera vez que recuerdo haberla visto en asociación con una llamada al open
. La documentación 5.10.1 tiene 52 usos de ejemplo de open
que implica manejadores de archivos léxicos, pero solo dos tienen paréntesis con my
.
Se vuelve más curioso:
$ 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.
Los paréntesis que faltan, por lo que ¿dónde está la advertencia ?!
Adición de un pequeño punto y coma, sin embargo, hace advertir sobre la existencia paréntesis que faltan:
$ 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.
Veamos en fuente de Perl para ver donde el aviso viene.
$ grep -rl 'Parentheses missing' .
./t/lib/warnings/op
./op.c
./pod/perl561delta.pod
./pod/perldiag.pod
./pod/perl56delta.pod
Perl_localize
in op.c -que maneja my
, our
, state
y local
: contiene el siguiente fragmento:
/* 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");
}
Aviso el comentario de la primera línea. En My Life With Spam, Mark Dominus escribió: "Por supuesto, esto es una heurística, que es una forma elegante de decir que no funciona". La heurística en este caso tampoco funciona y produce una advertencia confusa.
El condicional
if (sigil && (*s == ';' || *s == '=')) {
explica por qué perl -we 'open my $fh, $file'
no avisa, pero lo hace con un punto y coma final. Mire lo que ocurre con un código similar pero sin sentido:
$ 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.
Recibimos la advertencia! El open
caso 3-argumento no advierte porque "<"
impide sigil
se convierta en verdad, y el modificador de or die ...
pasa reunir, en términos obtusos, porque el token or
comienza con un carácter distinto de ;
o =
.
Parece que la intención de la advertencia es proporcionar una sugerencia útil sobre cómo corregir el código que de otro modo produciría resultados sorprendentes, p.,
$ 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
Aquí, la advertencia hace tiene sentido, pero el caso es que has encontrado una fuga en la heurística.
menos es más
Perl tiene azúcar sintáctico que hace que la escritura Unix-style filters fácil, como se explica en la documentación perlop.
La nula Filehandle <>
es especial: se puede usar para emular el comportamiento de sed y awk. La entrada desde <>
proviene de la entrada estándar o de cada archivo listado en la línea de comando.Así es como funciona: la primera vez que se evalúa <>
, se comprueba la matriz @ARGV
, y si está vacía, $ARGV[0]
se establece en "-"
, que cuando se abre proporciona la entrada estándar. La matriz @ARGV
se procesa como una lista de nombres de archivo. El bucle
while (<>) {
... # code for each line
}
es equivalente a la siguiente Perl-como pseudo código:
unshift(@ARGV, '-') unless @ARGV;
while ($ARGV = shift) {
open(ARGV, $ARGV);
while (<ARGV>) {
... # code for each line
}
}
Utilizando el gestor de archivo null (también conocido como el operador de diamante) hace que su comportan código como la utilidad grep Unix .
- filtro de cada línea de cada archivo con el nombre en la línea de comandos o
- filtro de cada línea de la entrada estándar cuando se da solamente un patrón
El operador de diamantes también se encarga de al menos un caso esquina que tu código no. Observe a continuación que la barra está presente en la entrada pero no aparece en la salida.
$ cat 0
foo
bar
baz
$ ./mygrep bar 0
Parentheses missing around "my" list at ./mygrep line 10.
¡Siga leyendo para ver cómo el operador de diamantes mejora la legibilidad, la economía de expresión y la corrección!
mejoras recomendados a su código
#! /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/;
}
En lugar de codificar el camino a perl
, utilice env
respetar PATH del usuario.
En lugar de asumir ciegamente que el usuario proporcionó al menos un patrón en la línea de comandos, verifique que esté presente o brinde una guía de uso útil en caso contrario.
Porque su patrón vive en una variable, podría cambiar. Esto no es profundo, pero eso significa que es posible que sea necesario volver a compilar el patrón cada vez que su código evalúe /$pattern/
, , es decir,, para cada línea de entrada. El uso de qr//
evita este desperdicio y también brinda la oportunidad de verificar que el patrón que el usuario suministró en la línea de comando sea un regex válido.
$ ./mygrep ?foo
./mygrep: bad pattern (?foo):
Quantifier follows nothing in regex; marked by <-- HERE in
m/? <-- HERE foo/ at ./mygrep line 10.
El lazo principal es tanto idiomático como compacto. La variable especial $_
es el argumento predeterminado para muchos de los operadores de Perl, y el uso juicioso ayuda a enfatizar el que en lugar de cómo funciona la maquinaria de la implementación.
Espero que estas sugerencias ayuden!
Siempre, * siempre *, ** siempre ** comprobar si 'abrir' tiene éxito! –
use tres argumentos [abrir] (http://perldoc.perl.org/functions/open.html) –