miércoles, 22 de julio de 2015

Como migrar de blogspot.com a un blog estatico con dos scripts de perl

Armé este script por que necesitaba migrar mi blog de blogspot,
tengo escritos 10 años y es mucho contenido para migrar manualmente.
Probé unos programas que existen actualmente, en internet, no recuerdo
el nombre en este momento, pero si bien dicen hacer lo que necesito, la realidad
es que no funcionaban a la fecha (Diciembre 2014). Por eso en mis ratos libres
comencé a hacer este proyectito que puede serle de utilidad a otras personas

Estos dos scripts preparan los html del blog obtenido de blogspot
para que funcione de forma estática, sin requerir consultar imágenes de enlaces externos.

Son bienvenidas preguntas o posibles mejoras, por que lo testee muy poco.

Las instrucciones y los scripts estan en:

https://github.com/walt3rminer/blogspot2static

http://www.wallves.com/es/proyectos/

Primero se usa wget para bajar todo el blog, lo que pasa es que baja con muchos enlaces externos a imagenes y hojas de estilo.  Para ello se utiliza el script produce.pl que extrae solo los artículos y arma un índice.  Actualmente funciona con fechas en español e inglés, muy posiblemente haya blogs diferentes, asi que escribanme para que lo mejore!

BLOGNAME="poc-perl.blogspot.com.ar";
DIRSALIDA="output";
TMPFILE="ejemplo.html";

# Este comando puede tardar de acuerdo a cuanto contenido haya
wget -kr http://${BLOGNAME}/;

#wget -kr http://serverlinux.blogspot.com
# Setear los años para los cuales hemos escrito.
M=`seq 2005 1 2015`;

# Crear este directorio para almacenar la fotos del blog que seran bajadas
# con el siguiente script:
rm -rf externos
mkdir externos
# Borrar ejemplo.html
rm ${TMPFILE};
for i in $M; do
  find ${BLOGNAME}/$i -type f -exec ./produce.pl {} \; >> ${TMPFILE};
done

mkdir ${DIRSALIDA};
# summary.pl <nombre del html utilizado en el paso anterior> <directorio de salida>
./summary.pl ${TMPFILE} ${DIRSALIDA};

mv externos ${DIRSALIDA};

cp -r js ${DIRSALIDA};
cp -r css ${DIRSALIDA};

# Ahora a testear!
cd output
python ../httpserver.py

viernes, 21 de noviembre de 2014

PoL: Factorial

Factorial en PoL:

perl -le 'sub k { return ($_[0] * (k($_[0]-1))) if ($_[0] > 1);return 1;} print k($ARGV[0]);' 2

básicamente Factorail es:

n * n-1 * n-2 * ... * 1

Para hacer una funcion recursiva simplemente vemos

n * (n-1) * (n-2) ... * 1

Lo que basicamente indica la pate que uno tiene que usar como recursiva:

(n-1)

Y para perl

sub fact {
return  (  $_0] * (fact($_[0]-1)) if ($_[0] > 1) ;
return 1;
}


jueves, 4 de septiembre de 2014

usando demasiado una función/llamadas

Este artículo puede ser también pensado como un "buenas prácticas" o "pensar antes de usar un medio potencialmente lento".

Me he topado con un código que básicamente es:

sub get_info{
  # ldap requests
  return %user_info if ($valid_user)  ;
  if($error){
    return 1 if($network_error);
    return 0 if($user_error);
  }
}

Básicamente no es tan simple, pero da una idea de los códigos de retorno y de que datos se utiliza en caso de que esté todo bien.

El código donde se usa es:

sub makeSQL{
if(get_info() == 0){
  # something
}elsif(get_info() == 1){
  # something else
}else{
  %user = get_info();
  # balblabla
}

El problema es bastante simple, se llama a la función repetidas veces. Y lo peor de todo, es el orden. Usualmente, por lo que pude ver, el peor de los casos es RC==1 (no suele fallar tanto la red). Luego, la que es más usual que tenga datos (que sea un hash) es la última.

El código, para realmente simplificarlo, debería ser algo como:

#!env perl

sub get_info($){
  my %ans = ("arbol"    => "22",
              "kamisama" => "dios",
              "gohan"    => "arroz");

  return \%ans unless(($_[0] == 1) || ($_[0] == 0));
  return \$_[0];
}

my @params = (0,1,3);
my $result = {};
foreach my $para (@params){
  $result =  get_info($para);
  if($result=~/HASH/){
    use Data::Dumper;
    print "result: " . Dumper($result) . "\n";
  }else{
    print "## result: " . ($$result) . "\n";
  }
}

De esta forma se reduce significativamente las llamadas a get_info, usando una variable únicamente las variables y no volviendo a llamar getinfo.

Es obvio que elllamar a get_info es mucho más simple y cómodo, pero a la hora de realmente programar, no se debería tomar tan a la ligera las llamadas a las funciones, sobre todo, funciones que tienen IO o usan redes.

miércoles, 7 de marzo de 2012

Ejecutar un SHELL script con TIMEOUT

En la vida de todo sysadmin en algun momento hay que lidiar con "malos programadores" de shell scripts. Un problema que surgió en mi laburo fue el de monitorear unos scripts que quedaban colgados por un tiempo mayor al esperado y generaban problemas en las ejecuciones posteriores. En realidad lo que sucedia era que un fs tenia 65.000 archivos y el script ejecutaba un "ls", quedando colgado. Por supuesto la solución mas facil hubiese sido agregar una rutina de depuración al fs para evitar juntar tanta basura.... pero no... es mejor matar el script que se colgó y despues forzar la ejecución (!!!!). En fin, un hack de pocas lineas con PERL para zafar y que seguramente puede ser mejorado.
#! /usr/bin/perl

eval {
        local $SIG{ALRM} = sub { die "Me voy por timeout\n" };
        alarm 5;
        system("@ARGV");
        alarm 0;
};

if ( $@ )
{ print "$@";exit 1; }
La idea es ejecutar el comando que se quiera controlar por timeout de la siguiente manera:
./alarm.pl sleep 100
Me voy por timeout

viernes, 18 de noviembre de 2011

Pedido de articulos

Hace un tiempo que estamos poniendo algún que otro artículo en este blog. Y la verdad es que (en lo personal), las ideas de escribir cosas se me agotó. Con lo que, si alguien tiene dudas sobre algo en particular de Perl, o tiene una pregunta (como resolver el cifrar un socket en perl, como mandar mensajes XML a un webservice, etc, etc), postee un comentario a este post y veremos de investigar y hacer un artículo.

convirtiendo AM,PM a formato 24 horas (con Perl)

Aprovecho este post para mostrar también como se podría ejecutar un código dentro de una expresion regular.

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

Poniendo el despertador...

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" .

Variables especiales:

%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:

  1. El valor de salida del subproceso.
  2. La señal, si existiese una, que recibio el subproceso.
  3. Si se generó core dump o no (boolean) .
Hay que tener en cuenta que los valores que contiene esta variable son bits, por lo tanto deberemos utilizar los operadores de bit sobre la misma.En la man page se explica con detalles.

Ejecutando comandos externos.

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
Libros de OREILLY:
  • Mastering PERL
  • PERL CookBook