viernes, 18 de noviembre de 2011
Pedido de articulos
convirtiendo AM,PM a formato 24 horas (con Perl)
#!/usr/bin/perl
sub nonAMPM {
my $hour = $2;
if ($4 eq "PM") {
$hour = $hour+12;
}
return "$1 $hour:$3 $4";
}
$a= "11/18/2011 7:29:14 PM";
print "$a, ";
$a =~ s/(.*) (.*):(.*:.*) (..)/&nonAMPM/e;
print "$a\n";
Saludos
jueves, 3 de noviembre de 2011
Alarmas y subprocesos en PERL
En determinadas ocasiones se da la situación de necesitar ejecutar un comando de sistema desde PERL, bien para capturar su salida y usarla para algo o bien descartarla. Si no tenemos cuidado y controlamos lo que estamos ejecutando podemos caer en el error de disparar procesos que queden colgados e interrumpir el flujo principal de nuestro programa, o peor aún podemos no advertir que el comando que pedimos ejecutar falló por algun error y el programa principal no recibió alerta alguna continuando como si todo fuese normal. Para salvar estos inconvenientes vamos a usar la instrucción eval y la señal ALRM.
Veamos ejemplo.pl.#! /usr/bin/perl use strict; my $log; my $status; eval { local $SIG{ALRM} = sub {die "timeout\n"}; alarm 15; $log = `micomando.sh` || die; $status = $?>>8; alarm 0; }; if ( $@ ) { if ( /timeout/ ) { print "Salida por timeout\n"; } else { print "Comando fallido\n". "Valor en '\$\!': $!\n". "Valor en '\$\@': $@\n"; } } else { print "Ningun error\nLog: $log\n"; }
Comandos a utilizar.
eval:
Ejecutara el bloque de codigo contenido entre las llaves y si se produce un error podremos realizar la evaluacion necesaria usando la variable especial $@, esto se conoce como "Excepciones". Cuando se produce la "excepción" tendremos la posibilidad de ejecutar algun codigo destinado a brindar mas informacion al usuario para indicarle que fue lo que salio mal.
alarm:
El comando alarm toma como argumento el nro de segundos en el que se producira la llamada. Es decir en este caso 15 segundos. Pasado ese tiempo se dispara la llama a la señal ALRM y por consiguiente se ejecuta el codigo indicado entre llaves die "timeout\n" .
%SIG
Hash interno de PERL que contiene "signal handlers". Es decir podemos asignar una funcion o bloque de codigo a una señal en particular. Que es exactamente lo que se hace en esta linea local $SIG{ALRM} = sub {die "timeout\n"}; concretamente esta linea indica que cuando se produzca la señal ALRM se ejecutara el codigo que esta entre llaves. Al ejecutarla con local nos aseguramos de que la modificación no se propague por el resto del programa y permanezca dentro del bloque eval.
$@
Otra variable interna de PERL que captura el mensaje de error del ultimo bloque eval, aqui se almacenara el mensaje de error que arroje el bloque eval en el caso de producirse una falla.
$!
Variable interna de PERL, esta variable toma un valor cuando una llamada al sistema falla. El valor solo es valido inmediatamente despues de la llamada que falló.Esta variable tiene una forma particular de comportarse, si se la invoca como un string, devolvera el mensaje en formato "human readable" pero si se la invoca en forma numerica devolvera el valor de la constante definida en errno.h.
Un experimento interesante para realizar es printf("%d: %s\n", $!, $! ); y podremos ver ambos valores.
$?
Variable inerna de PERL, esta variable es bastante especial en el sentido de que en realidad es una palabra de 16 bits (o 2 bytes) que almacena diferente información dependiendo del caso.
La información que devolverá es la siguiente:
- El valor de salida del subproceso.
- La señal, si existiese una, que recibio el subproceso.
- Si se generó core dump o no (boolean) .
Cuando comienza a correr el script ejecuta el codigo que se encuentra en el bloque eval, carga la funcion anonima en el handler ALRM y setea la alarma para dentro de 5 segundos. Luego procede a lanzar el programa "micomando.sh" seguido del operador "or", tambien indicado como ||, y la instrucción die. Seguidamente se captura la salida del comando en la variable $log y el status de la ejecucion en $status y se procede a poner la alarma nuevamente en 0.
En el caso de que el programa "micomando.sh" quede en espera por mas de 5 segundos, el programa principal toma el control y ejecuta el comando die con el mensaje "timeout". Una aclaración aqui, el comando lanzado de fondo sigue corriendo, el die no lo matará sino que simplemente devolverá el control al flujo principal con la excepción. Una buena modificación seria obtener el PID del proceso lanzado y enviarle una señal SIGKILL para matarlo si hemos detectado que no responde.
En el caso de que la ejecución falle, por ejemplo por falta de permisos o porque el comando no existe, se ejecutará die y saldrá tambien con una excepción pero el error ya no será "timeout", si examinamos la variable $@ veremos que tiene el mensaje "Died at ./child_die.pl line 11". Continuando con la ejecución se puede ver que se declara la variable $status pero no se usa en ninguna parte del script. En realidad en la segunda entrega voy a agregar mas codigo para hacer un poco mas inteligente el script y que pueda tomar alguna decisión con respecto al status que devolvio el comando ejecutado. Por ahora solo quedara definida la variable y almacenará el status de la ejecución del ultimo comando si este fue exitoso ( que será siempre 0 ); caso contrario nunca se llenará ya que por ahora sale en la linea anterior con el die.
Esto es solamente una introducción al tema, PERL tiene documentación muy interesante y muy variada al respecto y hay capítulos enteros dedicados a esto, pero para implementar algo rapido y sencillo sirve.
Mas info en paginas del manual de PERL a traves de perldoc:- perlipc
- perlvar
- perlfunc
- Mastering PERL
- PERL CookBook
miércoles, 4 de mayo de 2011
pensando expresioens regulares para que no exageren en lo que abarcan
$a="hola mundo, esto es un planeta, chau mundo";
Este string es bastante simple, y ahora el siguiente regex:
/hol (.*) mundo /x
Esto es lo que resultaría (dentro de $1):
"a mundo, esto es un planeta, chau "
(sin las comillas, lo puse para que se vean los espacios)
Por defecto, el * es glotón, esto quiere decir, que va a intentar agarrar lo más posible, o sea, para nuestro caso, el último mundo.
Para evitar esto, y que tengamos algo más simple:
/ hol(.*?) mundo.* /x
El resultado de esto:
"a "
Que, en la mayoría de los casos, es lo que se intentaba obtener.
miércoles, 27 de abril de 2011
escribiendo regex más claras
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
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
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
$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
if ( grep { $_ eq "$tmpuserid" } @PredictedProductBuyers) {
}
buscar algo en un código (expresiones regulares básicas)
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.