2011-04-08 15 views
15

Es mi primer día para perl, y encuentro esta advertencia muy confusa.

Los paréntesis que faltan en torno a "mi" lista en línea de ./grep.pl 10.

Parece

open FILE, $file; 

funciona bien.

Lo que está mal con

open my $fh, $file; 

Gracias!

#!/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

Siempre, * siempre *, ** siempre ** comprobar si 'abrir' tiene éxito! –

+7

use tres argumentos [abrir] (http://perldoc.perl.org/functions/open.html) –

Respuesta

31

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:

  1. el programa que se quejó ($0)
  2. lo que trató de hacer ("open $file")
  3. 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!

+1

Greg, ciertamente siempre uso parens en mis ejemplos abiertos en mis tutoriales. Lo hago porque la precedencia es muy difícil de recordar. Siempre me equivoco con 'o' y' // 'y otras cosas, así que uso las conjunciones C que realmente entiendo. Y, por lo tanto, siempre uso parens. Lo hace más fácil de leer, también. – tchrist

+0

¿Puedes explicar el motivo del desvío "-" en @ARGV en la explicación de <>? Soy un principiante de Perl. ¡Gracias! – Alby

+1

@Alby Tenga en cuenta que es condicional y solo ocurre cuando @ARGV está vacío. Perlas mágicas abiertas de Perl: como sinónimo de la entrada estándar, desde la que se lee el manejador de archivos vacío o "operador de diamante" cuando no hay argumentos en la línea de comando. –

16

my es para declarar una variable o una lista de ellos. Es un error común en Perl escribir

my $var1, $var2, $var3; 

para declarar todas ellas. La advertencia debe aconsejar que utilice la forma correcta: (¿no le dieron los errores o resultados erróneos, ¿verdad)

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

En el ejemplo del código hace exactamente lo que quiere, pero para que sea absolutamente claro se puede escribir

open my ($fh), $file; 

Aunque se podría argumentar que poner my en el medio de la línea es como ocultarlo. Tal vez sea más legible:

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

Ya veo. Gracias ! – zjk

+0

+1 para ponerlo en su propia línea ... es más fácil darse cuenta de esa manera. –

2

Para obtener una explicación más detallada de los mensajes de advertencia, utilice perldoc diagnostics. Por ejemplo,

use strict; 
use warnings; 
use diagnostics; 

my $fh, $file; 

generará la siguiente explicación útil:

Los paréntesis que faltan en torno a "mi" lista (W paréntesis) Usted dijo algo así como

my $foo, $bar = @_; 

when you meant 

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

Remember that "my", "our", and "local" bind tighter than comma. 

Usted También puede consultar la documentación para my en el símbolo del sistema:

perldoc -f my 

Si aparece más de un valor, la lista deben ser colocados entre paréntesis.

1

El problema real es que omitir llamadas de función es bastante frágil. Espera errores extraños si lo haces.

$ 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

Lo es. Recomiendo usar parens para todas las llamadas a funciones. – tchrist

1

Me parece que su código es más largo de lo necesario: debería emplear más pereza.

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

Si usted decide que necesita números de línea, o nombres de archivos (tal vez si hay más de un archivo), o alguna otra impresión más complicado, entonces es posible que desee escribir las cosas. Pero creo que el código que muestro es equivalente al código que muestra.

Normalmente, agregaría use strict; y use warnings; al código. Sin embargo, en este ejemplo, la única variable nombrada se define con my (así de estricto no ayudará), y no hay nada que advierta. Sin embargo, si está aprendiendo Perl, o si el programa es mucho más complejo que este, agregaría las líneas use, incluso después de unos 20 años de uso de Perl.

0

Puede estar haciendo una escuela o un proyecto de aprendizaje. Pero cuando quiero hacer algo como esto es perl, usualmente usaré esta versión más concisa de tu programa.

Perl -ne 'impresión si/your_regex /' your_file_list

Para obtener más información Probar

perldoc perlrun

y buscar las explicaciones de -n -p.

Cuestiones relacionadas