Archive for the 'Perl' Category

Generando contraseñas aleatorias con Perl

El día de hoy se manifestó la necesidad de generar una serie de claves aleatorias para un proyecto en el que me he involucrado recientemente, la idea es que la entrada que tenemos corresponde más o menos al siguiente formato:

username_1
username_2
...
username_n

La salida que se desea obtener debe cumplir con el siguiente formato:

username_1 pass_1
username_2 pass_2
username_3 pass_3
...
username_n pass_n

En este caso debía cumplir un requisito fundamental, las contraseñas deben ser suficientemente seguras.

No pensaba en otra cosa que usar el lenguaje de programación Perl para realizar esta tarea, así fue, hice uso del poderío que brinda Perl+CPAN y en menos de 5 minutos ya tenía la solución al problema planteado, el tiempo restante me sirvió para comerme un pedazo de torta que me dió mi hermana, quien estuvo de cumpleaños el día de ayer.

En primer lugar, debemos instalar el módulo String::MkPasswd, el cual nos permitirá generar contraseñas de manera aleatoria. Si usted disfruta de una distribución decente como Debian1 la instalación del módulo es realmente trivial.

# aptitude install libstring-mkpasswd-perl

Además, si usted se detiene unos segundos y lee la documentación del módulo String::MkPasswd2, se dará cuenta que la función mkpasswd() toma un hash de argumentos opcionales. Si no le pasa ningún argumento a esta función estará generando constraseñas aleatorias con las siguientes características:

  • La longitud de la contraseña será de 9.
  • El número mínimo de dígitos será de 2.
  • El número mínimo de caracteres en minúsculas será de 2.
  • El número mínimo de caracteres en mayúsculas será de 2.
  • El número mínimo de caracteres no alfanuméricos será de 1.
  • Los caracteres no serán distribuidos entre los lados izquierdo y derecho del teclado.

Ahora bien, asumiendo que el fichero de entrada es users.data, la tarea con Perl es resumida en una línea de la siguiente manera.

perl -MString::MkPasswd=mkpasswd -nli -e 'print $_, " ", mkpasswd()' users.data

La línea anterior hace uso de la función mkpasswd del módulo String::MkPasswd para generar las contraseñas aleatorias para cada uno de los usuarios que se encuentran en el fichero de entrada users.data, además, la opción -i3 permite editar el fichero de entrada in situ, ahora bien, quizá para algunos paranoicos (me incluyo) no sea suficiente todo esto, así que vamos a generar contraseñas aleatorias aún más complicadas.


#!/usr/bin/perl -li

use strict;
use warnings;
use String::MkPasswd qw(mkpasswd);

while(<>){
        chomp;
        print $_, " ", mkpasswd(
                -length => 16,
                -minnum => 5,
                -minlower => 5,
                -minupper => 3,
                -minspecial => 3,
                -distribute => 1
        );
}

En esta ocasión el la función mkpasswd() generará claves aún más complejas, dichas claves cumplirán con las siguientes condiciones.

-length

La longitud total de la contraseña, 16 en este caso.
-minnum
El número mínimo de digitos. 5 en este caso.
-minlower
El número mínimo de caracteres en minúsculas, en este caso 5.
-minupper
El número mínimo de caracterés en mayúsculas, en este caso 3.
-minspecial
El número mínimo de caracteres no alfanuméricos, en este caso será de 3.
-distribute

Los caracteres de la contraseña serán distribuidos entre el lado izquierdo y derecho del teclado, esto hace más díficil que un fisgón vea la contraseña que uno está escribiendo. El valor predeterminado es falso, en este caso el valor es verdadero.

El script mostrado anteriormente lo podemos reducir a una línea, aunque preferí guardarlo en un fichero al que denomine genpasswd.pl por cuestiones de legibilidad.

  1. Recuerde, Debian es inexorable[regresar]
  2. Este modulo en particular no solo se encuentra perfectamente integrado con nuestra distribución favorita, sino que además sus dependencias están resueltas. Esto es una simple muestra del poderío que ofrece una distribución como Debian.[regresar]
  3. Para mayor información acerca de las distintas opciones usadas se le sugiere referirse a man perlrun[regresar]

El algoritmo de Dijkstra en Perl

Hace ya algunos días nos fué asignado en la cátedra de Redes de Computadoras encontrar el camino más corto entre dos vértices de un grafo dirigido que no tuviese costos negativos en sus arcos, para ello debíamos utilizar el algoritmo de Dijkstra. Además de ello, debía presentarse el grafo y la ruta más corta en una imagen, para visualizarlo de mejor manera.

Cuando un profesor te dice: No se preocupen por la implementación del algoritmo de Dijkstra, utilice la que usted prefiera, pero les agradezco que la analicen. Además, pueden utilizar el lenguaje de programación de su preferencia. Estas palabras te alegran el día, simplemente eres feliz.

Por algunos problemas que tuve con algunos módulos en Python, no lo hice en dicho lenguaje de programación, no vale la pena explicar en detalle los problemas que se me presentaron.

Lo importante de todo esto, es que asumí el reto de realizar la asignación en un lenguaje de programación practicamente nuevo para mí, aunque debo reconocer que la resolución no me causó dolores de cabezas en lo absoluto, a continuación algunos detalles.

En primer lugar, formularse las preguntas claves, ¿existe algún módulo en Perl que implemente el algoritmo de Dijkstra para encontrar el camino más corto?, ¿existe algún módulo en Perl que implemente GraphViz?

En segundo lugar, buscar en el lugar correcto, y cuando hablamos de Perl el sitio ideal para buscar es search.cpan.org, efectivamente, en cuestión de segundos encontre los dos módulos que me iban a facilitar la vida, Bio::Coordinate::Graph, para encontrar la ruta más corta entre dos vértices en un grafo y GraphViz, para pintar el grafo.

En tercer lugar, verificar en tu distro favorita, Debian, si existe un paquete precompilado listo pasa ser usado, para GraphViz existe uno, libgraphviz-perl, así que para instalarlo es tan fácil como:

# aptitude install libgraphviz-perl

Para instalar el módulo Bio::Coordinate::Graph, primero debía debianizarlo, eso es tan sencillo como hacer.

# dh-make-perl --build --cpan Bio::Coordinate::Graph

Luego debe proceder a instalar el fichero .deb que se generó con dh-make-perl, para lograrlo hago uso del comando dpkg, eso es todo. Por cierto, acerca del comando dh-make-perl ya había hablado en la entrada Perl: Primeras experiencias.

Encontrando el camino más corto

Según dice la documentación del módulo Bio::Coordinate::Graph debemos hacer uso de un hash de hashes anónimos para representar la estructura del grafo, en ese momento recordé algunas palabras que José Luis Rey nos comentó en la primera parte del curso de Perl, vale resalta la siguiente: Si usted no utiliza un hash, no lo está haciendo bien.

my $hash = {
		'6' => undef,
		'1' => {
			'2' => 1
			},
		'2' => {
			'6' => 4,
			'4' => 1,
			'3' => 1
			},
		'3' => {
			'6' => 1
			},
		'4' => {
			'5' => 1
			},
		'5' => undef
	};

Algo que es importante saber es que en Perl las estructuras de datos son planas, lo cual es conveniente. Por lo tanto, vamos a tener que utilizar referencias en este caso, pero luego nos preocuparemos por ello. Ahora solo resta decir que, la estructura hecha a través de un hash de hashes es sencilla de interpretar, las llaves o keys del hash principal representan los vértices del grafo, ahora bien, las llaves de los hashes más internos representan los vértices vecinos de cada vértice descrito en el hash principal, el valor de los hashes más internos representa el peso, distancia o costo que existe entre ambos vértices, para aclarar la situación un poco: El vértice 2, tiene como vecinos a los vértices 3, 4 y 6, con costos de 1, 1 y 4 respectivamente. Puede ver una muestra del grafo resultante.

De manera alternativa puede utilizar un hash de arrays para representar la estructura del grafo, siempre y cuando todos los costos entre los vértices sean igual a 1, este método es menos general que el anterior y además, este tipo de estructura es convertida a un hash de hashes, así que a la final resulta ineficiente.

Una vez definida la estructura del grafo corresponde crear el objeto, esto es realmente sencillo.

my $graph = Bio::Coordinate::Graph->new(-graph => $hash);

Lo que resta es definir el vértice de inicio y el vértice final, yo los he definido en las variables $start y $end, luego de ello, debemos invocar al método shortest_path, de la siguiente manera:

my @path = $graph->shortest_path($start, $end);

En el array @path encontraremos los vértices involucrados en el camino más corto.

Pintando el grafo

Una vez hallado el camino más corto entre dos vértices dados en un grafo dirigido con un costo en los arcos siempre positivos, lo que resta es hacer uso del módulo GraphViz, hacemos uso del constructor del objeto de la siguiente manera:

my $g = GraphViz->new();

Usted puede invocar al constructor con distintos atributos, para saber cuales usar le recomiendo leer la documentación del módulo.

Ahora bien, yo quería distinguir aquellos vértices involucrados en el camino más corto de aquellos que no pertenecían, así que lo más sencillo es generar una lista de atributos que será usada posteriormente. Por ejemplo:

my @shortest_path_attrs = (
				color => 'red',
			);

Una vez definidos los atributos, debemos generar cada uno de sus vértices y además, establecer los arcos entre cada uno de ellos. Para ello haremos uso de los métodos add_node, add_edge.

foreach my $key (keys %$hash){
	$g->add_node($key);
	foreach my $neighbor (keys %{$hash->{$key}}){
		$g->add_edge($key => $neighbor, label => $hash->{$key}->{$neighbor});
	}
}

El bloque de código anterior lo que hace es agregar cada uno de los vértices que se encuentran en el hash que representa la estructura del grafo, para cada uno de estos vértices, se añade cada uno de los arcos que lo conectan con sus vecinos, nótese el manejo del operador flecha (->) para desreferenciar las referencias al hash de hashes anónimos.

Una vez construidos los nodos y sus arcos, ¿cómo reconocer aquellos que pertenecen al camino más corto?, bueno, toda esa información la podemos extraer del array que hemos denominado @path.

for (my $count=0; $count < = $#path; $count++){
	$g->add_node($path[$count], @shortest_path_attrs);
	$g->add_edge($path[$count] => $path[$count+1], @shortest_path_attrs) unless ($count+1 > $#path);
}

Según dice la documentación del módulo GraphViz todos los atributos de un vértice del grafo no tienen que definirse de una sola vez, puede hacerse después, ya que las declaraciones sucesivas tienen efecto acumulativo, eso quiere decir lo siguiente:

$g->add_node('1', color => 'red');

Es equivalente a hacer las siguientes declaraciones sucesivas.

$g->add_node('1');
$g->add_node('1', color => 'red');

Lo que resta por hacer en este instante es exportar el objeto creado, puede crear por ejemplo un PNG, texto sin formato, PostScript, entre otros.

Yo decidí generar un fichero PostScript:

$g->as_ps("dijkstra.ps");

Por supuesto, les dejo una muestra de los resultados. Los vértices cuyo borde es rojo, son aquellos involucrados en el camino más corto desde el vértice inicio al vértice final, los arcos que marcan la ruta más corta también han sido coloreados.

Como siempre, todas las recomendaciones, comentarios, sugerencias son bienvenidas.

Perl y su poderío

Al igual que José, considero que el hilo de discusión Borrar línea X de un archivo es bastante interesante, este hilo fué discutido en la lista de correos técnica 1 del Grupo de Usuarios de Linux de Venezuela (VELUG), el problema planteado 2 consistía en eliminar un registro completo, en donde se pasara como argumento el primer campo (tal vez el identificador) de dicho registro.

Suponga que el fichero tiene la siguiente estructura:

:(123
	... # otros campos
)

:(234
	... # otros campos
)

:(456
	... # otros campos
)

Se dieron soluciones en lenguajes como Bash y C 3 y ciertas en Perl, éstas últimas son las que llaman mi atención, vamos por partes 4.

José propuso lo siguiente:

#!/usr/bin/perl -n
$deadCount = 7 if ($_ =~ /${ARGV[0]}/);
--$deadCount if ($deadCount);
print unless ($deadCount);

El programa debe ejecutarse así:

$ perl script.pl archivo 123

Este programa hace el trabajo, pero al final emitirá un error porque cree que el argumento 123 es otro fichero, y por supuesto, no lo encuenta.

Mi solución fue la siguiente:

perl -ne 'print unless /:\\(123/../\\)/' input.data > output.data

Por supuesto, en este caso solamente estaría eliminando el registro cuyo primer elemento es 123, funciona, pero genera un problema al igual que el hecho por José, se deja una línea de espacio vacía adicional en los registros, cuando el separador de los grupos de datos debe ser una 5 línea en blanco, tal cual como apunto Ernesto Hernández en una de sus respuestas.

Otro apunte realizado por el profesor Ernesto, fué que las soluciones presentadas hasta el momento de su intervención fué el análisis de fondo que estabamos haciendo, el problema no consistía en procesar cada una de las líneas, el trabajo en realidad consistía en analizar un registro multilínea (o párrafo), en términos más sencillos, cada grupo de datos (registros) está separado por una línea en blanco.

El profesor continuaba su excelente explicación diciendo que el problema se reduce al analizarlo de esta manera en lo siguiente:

…si se cuenta con un lenguaje de programación que está preparado para manejar el concepto de “registro” y puede definir el separador de registro como una línea en blanco, simplemente se trata de ignorar aquellos registros que tengan la expresión regular (X, donde X es la secuencia de dígitos que no nos interesa preservar.

La solución presentada por el profesor Ernesto fue:

perl -000 -i -ne 'print unless /\\(XXX/' archivo

En donde se debe sustituir las XXX por los dígitos cuyo bloque no nos interesa conservar.

De este hilo aprendí cosas nuevas de Perl, en realidad estoy comenzando, muchos pensarán que este código es críptico, por ello considero conveniente aclarar algunas cosas.

Lo críptico de un código no es inherente a un lenguaje particular, eso depende más del cómo se programe.

En este caso particular una sola línea de código nos proporciona mucha información, evidentemente para comprender dicho contenido es necesario leer previamente cierta documentación del lenguaje, pero ¿quien comienza a programar en un lenguaje en particular sin haber leído primero la documentación?, la respuesta parece lógica, ¿cierto?.

La existencia de opciones predefinidas y maneras de ejecutar el interprete de Perl permiten enfocarse únicamente en la resolucion de tareas, cero burocracia.

Un ejemplo de lo mencionado en el párrafo anterior es el siguiente, un bucle lo puedo reducir con la opción de ejecución -n del interprete Perl, simplemente leyendo un poco perlrun6 nos enteramos del asunto, eso quiere decir que podemos reducir a una simple opción de ejecución del interprete de Perl todo esto:

#!/usr/bin/perl
while(<>){
#...
}

¿Qué es ese operador que parece un “platillo volador” 7 (null filehandle)?, bueno, lea perlop, en especial la sección I/O Operators.

La opción -i me permite editar (reescribir) in situ el fichero que vamos a procesar, en el caso de no añadir una extensión no se realizara un respaldo del fichero, el fichero original se sobreescribe. Mayor detalle en perlrun.

Lo que si no sabía hasta ahora, es lo explicado por el profesor acerca de los párrafos (registros multilínea) en Unix, la opción -0 tiene un valor especial, el cual es 00, si este valor es suministrado hace que Perl entre en “modo párrafo”, en pocas palabras, se reconoce una línea en blanco como el separador de los registros.

El resto del código es solo manejo de una sencilla expresión regular, se asume que el lector conoce algo del tema, solo indica el registro que queremos ignorar.

Así que podemos concluir lo siguiente, Perl no es críptico, asumiendo que el programador ha leido suficiente documentación acerca del lenguaje en cuestión, evitamos la burocracia y atendemos el problema de raíz en el menor tiempo posible.

La solución que propone Perl con el lema Hay más de una manera de hacerlo, es ofrecerle al programador libertad en su forma de expresarse, ¿acaso todos hablamos el mismo idioma?, ¿acaso debemos seguir las malas prácticas que intenta difundir el maligno Java?, coartar el pensar del programador y obligarlo a hacer las cosas al estilo Java, ¿dónde queda la imaginación? 8, ¿paso a ser un lujo?.

A todos los que lo deseen, les invito a participar en la lista de correos técnica (l-linux) del Grupo de Usuarios de Linux de Venezuela (VELUG), les recomiendo leer detenidamente las normas de uso antes de inscribirse en la lista.

  1. l-linux es la la lista de correos para Consultas Técnicas sobre Linux y Software Libre en VELUG[regresar]
  2. Quien inicio el hilo de discusión fue José Luis Bazo Villasante[regresar]
  3. ¿Con intención de autoflagelación?[regresar]
  4. Diría Jack El Destripador[regresar]
  5. No dos, ni tres, …[regresar]
  6. man perlrun, perlrun se incluye en la documentación de Perl, en sistemas Debian lo encontramos en el paquete perl-doc, para instalar simplemente hacer ejecutar el comando aptitude install perl-doc como superusuario[regresar]
  7. Según nos conto José Luis Rey, el profesor Ernesto Hernández le llama así de manera informal[regresar]
  8. De hecho, se dice que, un programador experto en Java está muy cerca de convertirse en un autómata[regresar]

Perl: Primeras experiencias

La noche del sábado pasado, después de terminar de estudiar con Ana la cátedra Programación Paralela y Distribuida, me dispuse a revisar las distintas instancias de Planeta Linux, como normalmente hago, pero eso no fué suficiente, me puse a validar el feed que se estaba generando en ese momento (en particular, el feed de la instancia venezolana). Me percate de varios errores y advertencias, entre ellos me llamo la atención:

This feed does not validate.

In addition, this feed has an issue that may cause problems for some users. We recommend fixing this issue.

line 11, column 71: title should not contain HTML (20 occurrences) [help]

Luego de leer la ayuda noto que es recomendable cambiar todos los nombres de las entidades html a su equivalente decimal, es decir, si tenemos por ejemplo: &copy; debe modificarse &#169;, de igual manera con el resto.

Tenía varias opciones, una de ellas era realizar el reemplazo masivo desde vim, pero esta tarea es realmente ineficiente por el hecho de tener que reemplazar todos los nombres de las entidades html a su equivalente decimal uno por uno, además de eso, debía hacerlo para las tres instancias presentes en Planeta Linux, primera opción descartada de entrada.

Aprovechando que la semana pasada, al igual que el profesor Francisco Palm, estuve presente en un curso sobre el lenguaje de programación Perl, el cual fué dictado por José Luis Rey con la ayuda de Daniel Rodríguez en las instalaciones de Fundacite Mérida, quería poner en práctica algunas de las cosas que aprendí en dicho curso.

Antes de continuar debo agradecer al profesor José Aguilar, a la ingeniera Blanca Abraham, y a la Sra. Tauka Shults por la oportunidad que me brindaron.

Una de las cosas que nos recalcó José Luis fué acerca de las virtudes que debía tener un programador, una de ellas debe ser la flojera, es decir, comenzar a escribir código realmente útil de inmediato, sin ningún requerimiento adicional como ocurre en lenguajes de programación como el C/C++, en donde es necesario realizar una serie de procedimientos antes de comenzar a escribir código útil.

Como mencione en el párrafo anterior, la idea es llegar a ser lo más productivo en el menor tiempo posible. Generar un hash de entidades de nombres html no me parecía el camino idóneo, así que recorde el tema de la flojera, sin pensarlo dos veces comence a buscar en search.cpan.org un módulo que me permitiera convertir los nombres de las entidades html a su equivalente decimal, como primer resultado obtuve lo que buscaba, el módulo HTML::Entities::Numbered, había encontrado mi salvación, leo un poco acerca de su uso y es más sencillo de lo que pensaba, siguiente paso, proceder a instalarlo.

Para debianizar un módulo en Perl es muy sencillo, en primer lugar debemos recurrir al comando dh-make-perl, si no lo tenemos instalado ya, debemos proceder como sigue:

# aptitude install dh-make-perl

Ahora ya podemos debianizar el módulo que requerimos, para ello tuve que realizar lo siguiente:

# dh-make-perl --build --cpan HTML::Entities::Numbered

Como lo puede apreciar, su uso es realmente sencillo, para una mejor explicación acerca de este último comando le recomiendo leer la entrada Instalando módulos de Perl en Debian escrita por Christian Sánchez, como era la primera vez que hacia uso del comando cpan tuve que configurarlo, esto no tomo mucho tiempo, el asistente ofrece explicaciones bastante detalladas.

Una vez realizado el proceso más complicado de toda la operación, el resto era escribir el código fuente que me permitiese convertir los nombres de las entidades html a su equivalente decimal, he aquí el resultado.

#!/usr/bin/perl -l

use strict;
use warnings;
use HTML::Entities::Numbered;

unless(open(INPUT, $ARGV[0])) { die "ERROR: No se especifico archivo para abrir. $!"; }
open(OUTPUT, ">$ARGV[0].bak");
while(<INPUT>){ print OUTPUT name2decimal($_) if chomp; }

¡Listo!, en tan pocas líneas de código he logrado resolver el problema, por supuesto, todo se redujo a buscar el módulo apropiado, una vez hecho los cambios a los ficheros de configuración de Planeta Linux procedí a actualizar la última versión en subversion.

Puede apreciar el antes y después de los cambios realizados.