miércoles, 27 de abril de 2011

escribiendo regex más claras

Como el ejemplo anterior (substitución con ejecución con lo obtenido), hay veces que las epresiones regulares que escribimos, sobre todo en un programa largo, terminan siendo algo engorroso. Para lo cual, Perl nos deja escribir las regex en múltiples líneas.

El anterior POL, podría ser reescrito como un archivo en disco:

#!/usr/bin/perl -pw

s@
(
  \d+
  (?:
    \.\d{3}
  )
)
@
"Fecha: " . scalar(localtime($1))
@ex

Esto básicamente hace los mismo, nada más que agrega "Fecha: " por delante de la hora que noosotros estamos parseando.

Notese que lla regex, teniendo mucho espacio, no le da importancia. Esto es lo que hace (básicamente) al usar el /x.

Básicamente, es una forma que voy a utilizar para poner algunas regex más complejas, e ir comentandola para que pueda ser leida más fácilmente por todos.

substitución con ejecución con lo obtenido

Hay varias veces, sobre todo procesando logs, que necesitamos verlo de mejor manera (por ejemplo, los logs del squid, que ponen la hora en epoch).

Ejemplo:
1303873213.829     21 127.0.0.1 TCP_MISS/200 2993 GET cache_object://localhost/info - NONE/- text/plain
1303873217.363     21 127.0.0.1 TCP_MISS/200 2993 GET cache_object://localhost/info - NONE/- text/plain

Para poder ver mejor los logs, simplemente con POL (Perl One Liner), es simple:

$ perl -pe 's/^(\d+(?:\.\d{3}))\s/scalar(localtime($1)) /e' access.log

La salida va a pasar a ser:

Wed Apr 27 00:00:13 2011    21 127.0.0.1 TCP_MISS/200 2993 GET cache_object://localhost/info - NONE/- text/plain
Wed Apr 27 00:00:17 2011    21 127.0.0.1 TCP_MISS/200 2993 GET cache_object://localhost/info - NONE/- text/plain

miércoles, 20 de abril de 2011

referencias en Perl

Muchas veces es necesario usar una función, y que modifique la variable que se le pasa, como lo hace el chomp. Eso se hace usando referencias en Perl.

El código de ejemplo es:

#!/usr/bin/perl -w


my $a = "hola";

sub NoRef{
  my $tmp = $_[0];

  $tmp =~ s/hola/chau/g;

  print "dentro: '" . $tmp . "'\n";

  return 0;
}

sub WithRef{
  my $tmp = \$_[0];

  $$tmp =~ s/hola/chau/g;

  print "dentro: '" . $$tmp . "'\n";

  return 0;
}

print "primero a es $a\n";

NoRef($a);

print "ahora es a es $a\n";

WithRef($a);

print "y por ultimo a es $a\n";
 

La explicación (gíbara) es, al usar referencias, la variable pasa a ser un puntero, con lo que la salida es:

primero a es hola
dentro: 'chau'
ahora es a es hola
dentro: 'chau'
y por ultimo a es chau

Esto es de utilidad, para entender el funcionamiento de funciones como uc o push. La primera no hace nada con la variable, regresa el resultado, pero la variable queda intacta. Mientras que la segunda, regresa el número de elementos del array, pero modifica el array que se le pasó agregando el listado de objetos pasados.

subrutina en una variable

Todavía no se bien que utilidad tiene, pero estaba jugando con variables, y me encontré esto

$a = sub { return $_[0] . " mundo"; }
print &$a("Mundo");

Lo que imprime
Hola Mundo

Sin el &, básicamente lo que imprime es:
CODE(0x6188e8)

domingo, 17 de abril de 2011

buscar algo en un código (expresiones regulares básicas) Parte #2

Continuando con el subject anterior, esta me pareció una solución elegante para buscar en un array algun item único:


if ( grep { $_ eq "$tmpuserid" } @PredictedProductBuyers) {
   ## Aca, hacer algo si el item fue encontrado,
   ## por ejemplo, contabilizar el producto.
   cuenta++;
}

buscar algo en un código (expresiones regulares básicas)

Algo básico para todo sysadmin es buscar en los logs por errores o avisos, y luego disparar alguna acción en base a eso.

Este ejemplo lo que hace es:
  • Obttener un bloque de texto (512 bytes, es una lectura que puede tener muchas líneass o no)
  • Buscar al principio de una línea, un string "HTTP..." (esto es una respuesta de HTTP básica)
  • si lo encuentra, disparar otra funcion

#!/usr/bin/perl -w

$text = "HTTP/1.0 302 Found
Date: Sun, 17 Apr 2011 04:29:37 GMT
Content-Length: 222
Content-Type: text/html; charset=UTF-8
Cache-Control: private
Set-Cookie: PREF=ID=1847c960b2585ff2:FF=0:TM=1303014577:LM=1303014577:S=nIWoylYn45zN4QZi; expires=Tue, 16-Apr-2013 04:29:37 GMT; path=/; domain=.google.com
GMT; path=/; domain=.google.com; HttpOnly
Server: gws
X-XSS-Protection: 1; mode=block
Location: http://www.google.com.ar/
";

if($text =~ /^HTTP/m){
  print "correcto\n";
}

En caso de que quiera evaluar que la primer línea tenga un HTTP al principio, simplemente hay que reemplazar la expresión regular con:

if($text =~ /^HTTP/){

Esto hace que se evalue solamente la primer línea solamente.