Perl
Sumario
Enlaces y referencias
- Recursos en este wiki:
- Páginas en la categoría Perl
- Desarrollo de módulos:
- Artículos temáticos:
Plantillas
En este wiki existe algunas plantillas útiles para cuando se crean documentación sobre Perl y sus módulos:
Documentación para un programa
Las siguientes páginas proporcionan ayuda sobre documentación:
- POD habla sobre el formato de la documentación.
- Pod::Usage es una herramienta que automatiza los textos de ayuda en programas
- Hay algunas plantillas para insertar en los fuentes de un programa.
y estas otras también pueden ser útiles:
- Gettext simplifica la traducción de textos
Configuración
Recursos X del depurador ptkdb
Para situar dentro de ~/.Xdefaults
o ~/.Xresources
, o para cargar directamente con xrdb
.
! Perl Tk Debugger XResources. ptkdb*scrollbars: sw ! controls where the code pane is oriented, down the left side, or across ! the top values can be set to left, right, top, bottom ptkdb*codeside: left ! Background color for the balloon ptkdb.frame2.frame1.rotext.balloon.background: blue ptkdb.frame2.frame1.rotext.balloon.font: -b&h-lucidatypewriter-medium-r-normal-*-20-*-*-*-m-*-*-* ! Hot Variable Balloon Font ptkdb.frame*font: -b&h-lucidatypewriter-medium-r-normal-*-20-*-*-*-m-*-*-* ptkdb.frame.menubutton.font: -b&h-lucidatypewriter-medium-r-normal-*-20-*-*-*-m-*-*-* ptkdb.frame2.frame1.rotext.font: -b&h-lucidatypewriter-medium-r-normal-*-20-*-*-*-m-*-*-* ptkdb.notebook.datapage.frame1.hlist.font: -b&h-lucidatypewriter-medium-r-normal-*-20-*-*-*-m-*-*-* ptkdb.notebook.subspage*font: -b&h-lucidatypewriter-medium-r-normal-*-20-*-*-*-m-*-*-* ptkdb.notebook.brkptspage*entry.font: -b&h-lucidatypewriter-medium-r-normal-*-20-*-*-*-m-*-*-* ptkdb.notebook.brkptspage*button.font: -b&h-lucidatypewriter-medium-r-normal-*-20-*-*-*-m-*-*-* ptkdb.notebook.brkptspage*button1.font: -b&h-lucidatypewriter-medium-r-normal-*-20-*-*-*-m-*-*-* ptkdb.notebook.brkptspage*checkbutton.font: -b&h-lucidatypewriter-medium-r-normal-*-20-*-*-*-m-*-*-* ptkdb.notebook.brkptspage*label.font: -b&h-lucidatypewriter-medium-r-normal-*-20-*-*-*-m-*-*-* ptkdb.toplevel.frame.textundo.font: -b&h-lucidatypewriter-medium-r-normal-*-20-*-*-*-m-*-*-* ptkdb.toplevel.frame1.text.font: -b&h-lucidatypewriter-medium-r-normal-*-20-*-*-*-m-*-*-* ptkdb.toplevel.button.font: -b&h-lucidatypewriter-medium-r-normal-*-20-*-*-*-m-*-*-* ptkdb.toplevel.button1.font: -b&h-lucidatypewriter-medium-r-normal-*-20-*-*-*-m-*-*-* ptkdb.toplevel.button2.font: -b&h-lucidatypewriter-medium-r-normal-*-20-*-*-*-m-*-*-* ptkdb.toplevel.button3.font: -b&h-lucidatypewriter-medium-r-normal-*-20-*-*-*-m-*-*-* ! Background color for where the debugger has stopped ptkdb*stopcolor: blue ! Background color for set breakpoints ptkdb*breaktagcolor*background: yellow ptkdb*disabledbreaktagcolor*background: white ! Font for where the debugger has stopped ptkdb*stopfont:-b&h-lucidabright-demibold-r-normal-*-20-*-*-*-p-*-*-* ! Background color for the search tag ptkdb*searchtagcolor: blue ptkdb*background: black ptkdb*foreground: white
La tipografía empleada es Lucida TypeWriter y he empleado el programa xfontsel del paquete x11-utils para obtener su nombre completo.
Perl en sólo una línea
Perl es utilizado en muchas tareas de administración de sistemas porque, entre otras ventajas, dispone de un nutrido grupo de parámetros de llamada del propio intérprete que hacen que éste haga cosas.
Dos fuentes de referencia para abrir boca:
- collection of one-liners, a part is adapted from such a list by Tom Christianson, one of the authors of "Programming Perl".
- perlrun
Y ahora vamos con esas cosas que se supone que el intérprete Perl hace automágicamente.
Ejecutar expresiones Perl: -e
Este parámetro, que puede repetirse varias veces, permite ejecutar expresiones Perl directamente. No se buscarán nombres de programa en los restantes argumentos y se añade un salto de línea al final de cada expresión.
$ perl -e 'print "Hello\n";' -e 'print " and goodbye\n";'
Procesar fin de línea automáticamente: -l
Habilita el proceso de final de líneas leídas, con las siguientes acciones:
- Aplica chomp() en todas las líneas cuando se usan los parámetros -n y/ó p
Lectura y proceso de la entrada: -n
Envuelve todas las expresiones de código alrededor de lo siguiente:
LINE: while (<>) {
# aqui va nuestro codigo
...
}
Es decir, lee la entrada estándar, línea a línea, y ejecuta el código indicado dentro del bucle.
Lectura y proceso de la entrada con copia a la salida estándar: -p
Envuelve todas las expresiones de código alrededor de lo siguiente:
LINE:
while (<>) {
# aqui va nuestro codigo
...
} continue {
print;
}
Lo que provoca que, tras ejecutar nuestro código, en cada pasada del bucle, envía a la salida estándar del proceso el contenido de la variable $_.
Partir la entrada en campos: -a
Utilizado con -p ó -n habilita un preproceso de cada línea de entrada, efectuando una división por espacios mediante la función split sobre el array @F. El separador puede cambiarse utilizando el parámetro -F.
while (<>) {
@F = split(' ');
... # aqui va nuestro codigo
}
Modificando archivos in situ: -i
Este parámetro habilita un modo especial en el que la construcción <> opera sobre los archivos in situ, modificándo su contenido, y con la posibilidad de efectuar una copia de seguridad sobre los mismos.
Es equivalente a la siguiente construcción:
#!/usr/bin/perl
$extension = '.orig';
LINE: while (<>) {
if ($ARGV ne $oldargv) {
if ($extension !~ /\*/) {
$backup = $ARGV . $extension;
}
else {
($backup = $extension) =~ s/\*/$ARGV/g;
}
rename($ARGV, $backup);
open(ARGVOUT, ">$ARGV");
select(ARGVOUT);
$oldargv = $ARGV;
}
.... # aqui va nuestro codigo
}
continue {
print; # this prints to original filename
}
select(STDOUT);
Para lograr esto, Perl realiza las siguientes tareas:
- Renombra el archivo original para conservar una copia de seguridad siempre que se proporcione una extensión a este parámetro:
- Si la extensión no contiene un asterisco (*) se añade al final del nombre del archivo como un sufijo.
- Si contiene uno ó más caracteres asterisco se sustituyen por el nombre del archivo original, de manera que es posible crear prefijos, sufijos ó incluso moverlos a otros directorios (siempre que éstos existan de antemano).
- Abre el archivo de salida hacia el nombre original.
- Selecciona dicho archivo como el destino predeterminado del operador print.
Es posible comprobar el final de cada fichero utilizando el operador eof.
Preguntas y respuestas
¿ Cómo comenzar un módulo Perl ?
Usando el programa module-starter que está incluido en el paquete Module::Starter con los parámetros adecuados para quitarnos trabajo de encima:
Parámetro | Argumento | Opcional | Uso y descripción |
---|---|---|---|
--module
|
name | No | Define el nombre del módulo |
--author
|
full_name | Sí | Nombre del autor |
--email
|
Sí | Dirección de correo del autor | |
--dir
|
path | Sí | Directorio donde se creará el árbol de trabajo. |
--builder
|
Module name | Sí |
Nombre del módulo Perl que emplearemos como constructor. Existen varias opciones que además pueden acortarse empleando otros parámetros:
|
--license
|
type | Sí | Tipo de licencia a utilizar con el módulo. Se reconocen por nombre las siguientes: perl, bsd, gpl, lgpl y mit. |
Árbol de trabajo
Aunque depende de factores como el módulo constructor definido -y los complementos instalados- el árbol contiene más o menos lo siguiente:
$ module-starter --module=Astillas --license=perl --builder=Module::Build \ --verbose --dir=astillas-perl Created astillas-perl Created astillas-perl/lib Created astillas-perl/lib/Astillas.pm Created astillas-perl/t Created astillas-perl/t/pod-coverage.t Created astillas-perl/t/pod.t Created astillas-perl/t/manifest.t Created astillas-perl/t/boilerplate.t Created astillas-perl/t/00-load.t Created astillas-perl/ignore.txt Created astillas-perl/Build.PL Created astillas-perl/Changes Created astillas-perl/README Created astillas-perl/MANIFEST Created starter directories and files $
- /lib
- Directorio raíz donde situará el código fuente Perl
- /t
- Directorio raíz de los programas de test
- Build.PL
- Archivo base de todo el proyecto
- Changes
- Registro de cambios
- README
- Primera documentación sobre el paquete
- MANIFEST
- Relación de contenido del paquete
- ignore.txt
- Archivo plantilla para excluir archivos del proyecto.
Configuración
El módulo Module::Starter admite configuración. Esta se sitúa en el archivo $HOME/.module-starter/config
(o en lo que indique la variable de entorno MODULE_STARTER_DIR) y consiste en una lista de pares nombre y valor separados por dos puntos. Si el valor contiene además una lista los elementos de ésta se separarán con espacios en blanco.
Los nombres corresponden a los valores que puede recibir un objeto Module::Starter y que coinciden con los parámetros del programa module-starter:
author: Victor Moral email: victor@astillas.net license: perl builder: mb ignores_type: git manifest
El último nombre define los dos sistemas SCM que vamos a emplear y para los que queremos crear listas de exclusión. En este caso son Git y el propio archivo Manifest.
Aviso: las variables desconocidas se ignoran sin advertencia alguna.
¿ Dónde instalar los módulos de mi aplicación ?
Hablando de Debian y ya que esta distribución adopta una jerarquía de archivos estándar podemos anotar lo siguiente:
- La jerarquía
/usr/lib
está pensada para alojar programas y componentes dependientes de la arquitectura donde se instale y datos de sólo lectura. - La jerarquía
/usr/share/
está indicada para componentes Perl puros, independientes de la arquitectura subyacente en ese caso, y para almacenes de datos de sólo lectura. - Se anima a las aplicaciones a que empleen directamente su propio directorio raíz dependiente de alguno o todos estos lugares.
Así que si tenemos una aplicación llamada vespasiano podemos incluir sus módulos Perl puros en /usr/share/vespasiano/
.
Empaquetando para Debian
Para realizar esta tarea existe la herramienta dh-make-perl del paquete del mismo nombre. Con ella es relativamente sencillo descargar un paquete desde el repositorio CPAN, extraerlo, comprobar las dependencias, añadir la parte debian/ e incluso construir el paquete final. Todo esto si no hay otros problemas por medio.
Para empezar a empaquetar un módulo como MooseX::Clone haríamos:
$ dh-make-perl --cpan MooseX::Clone
Conceptos
Prototipos
Los prototipos de funciones en Perl son un mecanismo limitado de comprobación de parámetros de funciones en tiempo de compilación. Estas declaraciones deben ser, pues, visibles en ese momento y afectan sólo a las llamadas a funciones con el estilo nuevo, en las que no se emplea el carácter &
como prefijo. Afectan por tanto a este ámbito por lo que se pueden seguir empleando de la forma antigua.
No tienen influencia con las referencias a subrutinas o a uso indirecto de las mismas:
\&foo
&{$foo_ref}
$foo_ref->()
Recetario
Ordenación numérica
Si se emplea la función interna sort los elementos se comparan como textos a menos que se emplee un bloque o una función donde poder variarlo usando los operadores adecuados.
En este caso:
my @ordered = sort { $a <=> $b } keys %lista;
el operador <=>
es el apropiado.
Textos multilínea en el código fuente
my $texto = <<EOF;
Esta es mi declaración
de disconformidad ...
.
.
y firmo y sello a fecha ...
EOF
Datos en archivos fuente
- Ref: perldata - Scalar value constructors
- Ref: SelfLoader
- Ref: Filehandles in Perl
Dentro de un archivo fuente Perl es posible incluir datos accesibles como si
de un archivo se tratase mediante el uso del token __DATA__
en una única línea en el fuente. Todo lo que aparezca tras ella no se considera parte del archivo fuente por lo que el intérprete Perl lo ignora.
Para acceder a su contenido se utiliza el símbolo DATA, que se define automágicamente en el módulo, de forma similar a ésta:
my $line = undef;
while (<DATA>) { $line .= $_; }
__DATA__
Primera línea de datos
Segunda línea de datos
...
Enésima línea de datos
Hay que entender que el símbolo DATA hace referencia en realidad al símbolo PACKAGE::DATA y que, por tanto, es similar a STDOUT y STDIN, los cuales referencian los descriptores de archivos estándar para la entrada y la salida. Perl se encarga de conectarlos en el script.
También a tener en cuenta:
- Existe otro símbolo también usado para ésto, __END__, pero como es un alias para main::DATA hace referencia únicamente al paquete principal y, obviamente, no se debe emplear en los módulos porque en realidad no estaría haciendo referencia al módulo (al espacio de nombres del módulo más bien), sino al espacio de nombres principal main.
- Sí se emplean los dos símbolos en un mismo fuente -__END__ para indicar el comienzo de la documentación y __DATA__ para el comienzo de los datos- los resultados serán erróneos en la lectura o en la generación de la documentación. En caso necesario se puede imbuir la documentación en el fuente usando
=pod
y=cut
y situar los datos al final del módulo.
Otro ejemplo más citando lo anterior:
package MyPackage;
use Pod::Parser;
sub extract {
my $parser = Pod::Parser->new();
# Aquí vamos a leer en realidad de <MyPackage::DATA>;
return $parser->parse_from_filehandle(\*DATA);
}
=pod
=encoding utf8
=head1 MyPackage
Esta es la auténtica documentación y bla,bla,bla.
=cut
1;
__DATA__
=head1 Mis funciones
Este texto es lo que lee el método I<extract> y con el que hace su trabajo ...
=head2 funcion1()
=head2 funcion2()
Acortando nombres de módulos
Cuando creamos módulos para una aplicación podemos encontrarnos ante el dilema de definir jerarquías de nombres largos y clarificadores pero pesados de emplear en los fuentes, o acortarlos hasta que sean manejables pero difíciles de distinguir. Existen un módulo en CPAN llamado aliased que nos permite usar jerarquías largas y manejables a un tiempo.
En el ejemplo siguiente la primera llamada crea e importa en el espacio de nombres del paquete activo una función cuyo nombre coincide con la última parte de la ruta indicada. En la segunda llamada le indicamos el nombre que nos resulte más cómodo para emplear después.
use aliased "MyCompany::Bancos::AEB::Norma58::RegistroPresentador";
my $registro = RegistroPresentador->new();
...
use aliased "MyCompany::Bancos::AEB::Norma58::RegistroPresentador" => 'Presentador';
my $registro = Presentador->new();
Sólo sirve para programación orientada a objetos dado que el módulo crea una función que devuelve el nombre completo de la clase y que se integra bien en el mecanismo de despacho de métodos. Para la programación procedimental el autor explica que tendría que usar algunos trucos empleando typeglobs y no ve la necesidad.
Fork
Libro: Learning Perl de Randall Schwartz (capítulo 14.4: Using fork )
El algoritmo básico al emplear fork es el que sigue:
my $child_pid;
if (!defined($child_pid = fork())) {
die "cannot fork: $!";
} elsif ($child_pid) {
# I'm the parent
} else {
# I'm the child
}
Primera letra en mayúsculas
Para transformar un texto de manera que pase de todo mayúsculas, por ejemplo, a sólo la primera letra de cada palabra conviene realizar una división de dicho texto en palabras y aplicar la función lcfirst a cada una.
my $texto = q(LACES TRANSPARENT IN BLISTER 060 CM ROUND FINE BLACK);
$texto = lc( $texto );
$texto =~ s/(^(\w)|\b(\w))/\U$1/g;
print $texto;
# Laces Transparent In Blister 060 Cm Round Fine Black
En el ejemplo cambiamos primero a minúsculas y luego lo separamos por palabras teniendo en cuenta aquellas que comienzan el texto o las que son precedidas por un espacio en blanco. Extraemos la palabra y ponemos en mayúsculas sólo la primera.
Como también recomiendan en la referencia anterior, se puede utilizar el módulo Text::AutoFormat de Damian Conway con mayor comodidad.
Ejemplo de uso:
use Text::AutoFormat;
my $text = q(LACES TRANSPARENT IN BLISTER 060 CM ROUND FINE BLACK);
my $formatted = autoformat $text, { case => 'highlight' });
print $formatted;
# Laces Transparent in Blister 060 Cm Round Fine Black
Adviértase que lo que el autor considera palabras triviales corresponden con el idioma inglés y no hay una forma sencilla (ni documentada) de cambiarlo.
Dependencias de otros módulos
- Ref: Module::ScanDeps
El programa scandeps parte del módulo Perl Module::ScanDeps y está diseñado para proporcionar una lista de dependencias de otros módulos Perl en un proyecto.
Dada la naturaleza de Perl es difícil obtener una lista precisa. El código puede cargarse dinámicamente e incluso construirser al vuelo por lo que las dependencias pueden ser sorpresivas.
El uso del programa es muy simple, se invoca sobre los fuentes Perl (uno o varios) sobre los que se pretenda obtener información, y el resultado es un bloque de texto que puede incluirse en el archivo de construcción del proyecto, ya sea un Build.PL o un Makefile.PL.
$ scandeps lib/MyModule.pm
'List::Util' => '1.47',
'Modern::Perl' => '1.20170117',
$
Los parámetros más comunes son los siguientes:
- -R: búsqueda no recursiva
- -c: compilar los fuentes e inspeccionar %INC depués
- -x: compilar y ejecutar el código para inspeccionar después %INC
Como desventaja podemos señalar que:
- Los resultados son módulo a módulo y no tienen en cuenta si pertenecen a una librería Perl
- Los números de versión son fijos, algo que puede dar problemas en el futuro.
Así que sirve como punto de partida pero no como herramienta final automática.
Permisos de archivos en octal y demás
Módulos de CPAN
En las siguientes secciones anoto qué módulos se pueden emplear para realizar tareas concretas. De esta forma consigo una relación de aquellos que me son más útiles según su naturaleza.
Desarrollo
- Test::More y otros que sirven para crear pruebas unitarias.
- Test::Net::Service permite cortocicuitar test dependiendo de la conectividad de un servidor.
- Module::Find permite encontrar y/o cargar módulos de o dentro de una subcategoría.
- Devel::Local modifica la variable
PERL5LIB
para usar otros repositorios en el desarrollo de otro.
Programación orientada a objetos
- Moose: creación de clases orientadas a objetos con ayuda de los siguientes extras:
- MooseX::StrictConstructor genera excepciones cuando un constructor recibe parámetros no declarados como atributos.
- MooseX::Singleton implementa el patrón Singleton o instancia única para una clase.
Archivos y directorios
- Sysadm::install: operaciones de instalación de archivos y directorios.
Configuración de programas
Registro de sucesos y errores
- Log::Log4perl es una implementación de Log4j en Perl.
Cálculos
Redondeo de cifras
La siguiente función soluciona un problema de redondeo con la función sprintf
dado que ésta emplea como principio el redondeo de la mitad hacia los números enteros pares más cercanos (véase la entrada de la Wikipedia al respecto). En los foros de StackOverflow he encontrado una respuesta a la que mejorado ligeramente y que consigue el tipo de redondeo que considero apropiado: de la mitad hacia la siguiente cifra sea ésta par o impar.
# Dada una función que redondea a 2 decimales por defecto
sub redondear {
my $cifra = shift;
my $decimales = shift || 2;
return _fix_sprintf( "%${decimales}f", $cifra);
}
sub _fix_sprintf {
my $format = shift;
my $value = shift;
# Si termina en cinco (los posibles ceros a continuación
# son despreciados)
if ($value =~ m/\d\..*50*$/){
# y el formato incluye un número concreto de cifras decimales
$format =~ /.*(\d)f$/;
if (defined $1){
# le añadimos una fracción de 5 al final para que sprintf redondee
# correctamente
my $coef = "0." . "0" x $1 . "05";
$value = $value + $coef;
}
}
$value = sprintf( "$format", $value );
return $value;
}
Es cierto que añade cierta sobrecarga en el proceso pero vista la ventaja de tener un método de redondeo de decimales preciso el precio no es alto.
Validación de datos
- CheckDigits es un módulo que permite validar diferentes códigos entre los que se encuentran:
- NIF o DNI español
- Código de Cuenta Cliente empleado por la banca española.
- Regexp::Common es un compendio de expresiones regulares que pueden utilizarse en cualquier parte de una aplicación.