2010-02-06 17 views
17

He estado utilizando la línea de comandos de Perl con una opción -ne durante años, en gran parte para procesar archivos de texto de maneras que sed no puede. Ejemplo:¿Qué debería saber todo hacker Perl sobre perl -ne?

cat in.txt | perl -ne "s/abc/def/; s/fgh/hij/; print;" > out.txt 

no tengo ni idea de dónde lo aprendí, y sólo he leído hoy perlrun y se encontró que hay otras formas (por ejemplo perl -pe).

¿Qué más debería saber sobre perl -ne?

+7

'perl -pe' es apropiado para su código de ejemplo. Use '-pe' y suelte la instrucción' print' – mob

+0

Gee, lo aprendí leyendo la salida de 'perl --help'. Tiendo a usar esa opción en todos los programas que uso, solo para saber qué hay disponible. –

+9

Bueno, todo está realmente en perlrun. Todos los hackers de Perl deberían leer la documentación. :) –

Respuesta

22

perl -ne 'CODE' es equivalente al programa

while (<>) { 
    CODE 
} 

perl -ane 'CODE' y perl -F/PATTERN/ -ane son también buenas expresiones idiomáticas para conocer. Son equivalentes a

while (<>) { 
    @F = split /\s+/, $_; 
    CODE 
} 

y

while (<>) { 
    @F = split /PATTERN/, $_; 
    CODE 
} 

Ejemplo: grep avanzada:

perl -ne 'print if/REGEX1/&&!/REGEX2/&&(/REGEX3/||/REGEX4/&&!/REGEX5/)' input 

perl -F/,/ -ane 'print if $F[2]==4&&$F[3]ge"2009-07-01"&&$F[3]lt"2009-08-01"' file.csv 


Un ejemplo particularmente inteligente que utiliza llaves coincidentes es here.

+0

Por curiosidad, ¿sabes lo que los poseyó para elegir '@ F'? –

+2

Supongo que "F" es para campos. Yo * creo * también podría ser una convención de awk. – Roboprog

+0

perldoc perlvar dice "La matriz @F contiene los campos de cada línea" –

4

La opción -i permite realizar las modificaciones en línea:

perl -i -pe 's/abc/def/; s/fgh/hij/' file.txt 

o guardar una copia de seguridad:

perl -i.bak -pe 's/abc/def/; s/fgh/hij/' file.txt 
6

Se puede especificar más de una cláusula -e. A veces tengo una línea de comando que comienza a crecer a medida que refino una operación de búsqueda/extracción/mangulación. si escribe mal algo, obtendrá un "número de línea" que le indicará cuál es el error.

Por supuesto, algunos podrían argumentar que si tienes más de una o dos -e cláusulas, tal vez deberías poner lo que sea en un guión, pero algunas cosas realmente son simplemente descartadas, entonces ¿para qué molestarse?

perl -n -e 'if (/good/)' -e '{ system "echo $_ >> good.txt"; }' \ 
-e 'elsif (/bad/)' -e '{ system "echo $_ >> bad.txt"; }' \ 
-e 'else' -e '{ system "echo $_ >> ugly.txt"; }' in.txt another.txt etc.txt 

Es de suponer que haría algo menos trivial que grep/egrep en 3 archivos :-)

2

me gusta pensar en perl -n como escoger bits específicos de la entrada y perl -p como map para todas las líneas de la entrada.

Como se ha observado, es posible conseguir el efecto de -p con -n, y podemos emular al revés:

$ echo -e "1\n2\n3" | perl -pe '$_="" if $_ % 2 == 0' 
1 
3

Saltarse las líneas con next parecería más natural, pero el código -p envolturas en

LINE: 
while (<>) { 
    ...  # your program goes here 
} continue { 
    print or die "-p destination: $!\n"; 
} 

Por diseño, next corre continue bloques:

Si hay un BLOQUE continue, siempre se ejecuta justo antes de que el condicional esté a punto de ser evaluado nuevamente. Por lo tanto, se puede usar para incrementar una variable de bucle, incluso cuando el bucle se ha continuado a través de la instrucción next.

El interruptor -l tiene dos efectos prácticos:

  1. Con -n y -p, automáticamente chomp cada registro de entrada.
  2. Conjunto $\ por lo que cada print agrega implícitamente un terminador.

Por ejemplo, para agarrar los primeros 10 puertos UDP mencionados en /etc/services que podría

perl -ane 'print $F[1] if $F[1] =~ /udp/' /etc/services | head

pero oops:

7/udp9/udp11/udp13/udp17/udp19/udp37/udp39/udp42/ud...

Mejor:

$ perl -lane 'print $F[1] if $F[1] =~ /udp/' /etc/services | head 
7/udp 
9/udp 
11/udp 
13/udp 
17/udp 
19/udp 
37/udp 
39/udp 
42/udp 
53/udp

Recuerde que -n y -p puede estar en la línea shebang también, así que para guardar el oneliner anterior como una secuencia de comandos:

#! /usr/bin/perl -lan 

BEGIN { 
    @ARGV = ("/etc/services") unless @ARGV; 
    open STDOUT, "|-", "head" or die "$0: head failed"; 
} 

print $F[1] if $F[1] =~ /udp/ 
12

hay una cosa importante a saber sobre perl -ne y perl -pe guiones: implícitamente utilizan <>.

"¿Por qué es tan importante?" puedes preguntar

El operador magic <> usa la forma de 2 arg de abierto. Si recuerda, 2 arg abierto incluye la especificación del modo con el nombre de archivo en un argumento. Una llamada de estilo antiguo al open FILE, $foo es vulnerable a la manipulación del modo de archivo. Un modo particularmente interesante en este contexto es |: abre un identificador a un conducto a un proceso que ejecuta.

Usted podría estar pensando "¡Gran cosa!", Pero lo es.

  • Imagine un trabajo cron ejecutado por root para archivos de registro munge en algún directorio.
  • La secuencia de comandos se invoca como script *.
  • Imagine un archivo en ese directorio llamado |rm -rf /.

¿Qué ocurre?

  1. El shell expande el * y tenemos script file_1 file_2 '|rm -rf /' file_4
  2. procesa la secuencia de comandos file_1 y file_2.
  3. A continuación, abre un identificador a STDIN de rm -rf /.
  4. Sigue mucha actividad de disco.
  5. file_4 ya no existe, por lo que no podemos abrirlo.

Por supuesto, las posibilidades son infinitas.

Puede leer more discussion of this issue at Perlmonks.

La moraleja de la historia: tenga cuidado con el operador <>.

FWIW, Acabo de confirmar que esto todavía es un problema con perl 5.10.0.

+0

Eso es algo serio. ¿Alguien ha dicho cuál sería la desventaja de usar el argumento 3 abierto, con un modo explícito de "lectura", si se utilizara para implementar el operador wokka <>? – Roboprog

+0

OK, leí el hilo perlmonks. Una minoría vocal insiste en que la locura es una característica. "Esperas que esto solo abra una lista de archivos para leer, pero hace más, y no nos importa que desees algo seguro, ortogonal y fácil". Es necesario que haya algún tipo de pragma/módulo de "diamante sane". Volví a subir esta respuesta, por encima de la mía, y espero que eventualmente se eleve a la cima de la lista. – Roboprog

+1

@Roboprog hay un módulo de CPAN: ['ARVG :: readonly'] (http://search.cpan.org/~davidnico/ARGV-readonly-0.01/lib/ARGV/readonly.pm). – rightfold

1

A menudo uso sed o awk pero me gusta mucho este perl correspondiente operación de matriz asesino:

$ cat my-input.txt 
git 111 HERE 2222 voila 333 
any 444 HERE none start 555 HERE 6 
svn 777 aaaa 8888 nothing 
two 222 HERE 9999 HERE 0000 

$ perl -nle 'print $a if (($a)=/HERE ([0-9]+)/)' my-input.txt 
2222 
6 
9999