Tutorial de perl
 
Juan Julián Merelo Guervós
Descargar el tutorial completo, en .zip y el tutorial completo, en .tgz
jmerelo@kal-el.ugr.es
 
 

1. ¿Qué es?

perl significa Practical Extraction and Report Language, algo así como lenguaje práctico de extracción y de informes. Es un lenguaje creado por Larry Wall (quien, por cierto, es uno de los net.gods más conocidos en la actualidad) con el objetivo principal de simplificar las tareas de administración de un sistema unix; en realidad hoy en día (en su versión 5.004) se ha convertido en un lenguaje de propósito general, y una de las principales herramientas de un buen internetero.

Larry Wall es el tipo de personas que han hecho de la Internet lo que es ahora: un lugar con libre intercambio de ideas, donde los que saben de algo, los gurus, ayudan al que no sabe. Larry (larry@wall.org) es un habitual del grupo de usenet comp.lang.perl, y es normal que el propio Larry conteste a una pregunta de un principiante o a un error con un parche para el mismo error.

Es un lenguaje que hereda estructuras principalmente de los intérpretes de comandos de unix, especialmente el csh, y de otras utilidades estándar, como awk y sed. En realidad, puede hacer todo lo que hacen cualquiera de ellos y todos ellos juntos, la mayoría de las veces de forma más simple, comprensible y fácil de depurar. Si alguna vez habeis tratado de escribir un script para el shell, sabéis lo que quiero decir.

perl es un lenguaje interpretado, aunque internamente funciona como un compilador. Por eso se habla de scripts, y no de programas, concepto referido principalmente a programas compilados al lenguaje máquina nativo del ordenador y sistema operativo en el que se ejecuta.

A pesar de que la versión actual del intérprete de perl es la 5, es muy habitual, sin embargo, encontrar la versión 4.036, el último patchlevel de la versión 4 y probablemente el más estable. Actualmente, la distribución 5 está en plena evolución (y ebullición), y viene acompañada de unos cuantos megas de parches. El consejo del autor es esperar un poco, por lo menos un par de patchlevels, hasta que se estabilice. La versión 5 es prácticamente compatible 100% con la 4; virtualmente todos los scripts que funcionan para la versión 4 lo hacen también en la 5. Los scripts presentados en este tutorial están desarrollados para la versión 4, pero deberían de funcionar, en principio, para la versión 5.

Aunque desarrollado originalmente en un entorno unix, actualmente hay versiones para casi todos los sistemas operativos: DOS (por ejemplo, la última versión, OpenDOS Windows NT, Amiga, MacOS (ver Bibliografía/Recursos.). Los scripts son compatibles entre las diversas plataformas, de forma que es un verdadero lenguaje multiplataforma. Muchos fabricantes lo incluyen en sus versiones de unix; también el Linux lo incluye. Si quieres saber si está en tu unix, escribe simplementeunix% which perl

/usr/local/bin/perly si contesta algo similar a lo anterior, es que está instalado en el sistema. En algún otro "sistema operativo", como Windows 95, acuérdate de si lo instalaste o no, debe de estar en algún lado en tu disco duro. Para coger la última versión, consultar el apartado
 

2. ¿Para qué sirve?
 

Prácticamente, sirve para todo. Todas las tareas de administración de unix se pueden simplificar con un programa en perl. Se usa también para tratamiento y generación de ficheros de texto. También hay proyectos completos y complejos escritos en perl, pero son los menos.

La forma del lenguaje facilita la programación rápida y sucia, el hacer un programa rápido para que funcione. Esto hace también que se utilice para hacer prototipos rápidos de algún algoritmo que queremos ver funcionar antes que tomarnos la molestia de programarlo en un lenguaje más eficiente, como el c++. Y últimamente ha encontrado su aplicación en la escritura de cgi (common gateway interface), o scripts ejecutados desde páginas de la World Wide Web. La mayoría de los programas que se encuentra uno para procesar formularios en la Internet llevan la extensión .pl, lo cual denota que están escritos en perl.

En general, los programas en perl se ejecutan en el servidor, como todos los programas CGI, a diferencia de otros programas ejecutados por el cliente (generalmente un navegador como el Internet Explorer de Microchof o el Navigator), como aquellos escritos en JavaScript o Java.

3. Mi primer programa en PERL

Escribir el archiconocido "Hola" en perl no puede ser más fácilprint "Passa, tio\n";Y eso es todo. No hay necesidad de abrir el programa, ni de cerrarlo, ni de incluir librerías estándar o no, ni nada de nada. Solamente hay que tener cuidado de terminar todas las sentencias con ;. \n tiene el mismo significado que en C; es decir, un retorno de carro(1).
 

Ejecutarlo es otro cantar; o mejor dicho, muchos cantares, una cantata entera. El perl siempre Hay Muchas Formas de Hacer Las CosasTM. Al menos tres, en este caso.
 

  1. Guarda el programa en un fichero, llamémoslo passa.pl. Se puede ejecutar conC:\PROGS\PERL>perl passa.pl
Passa, tio
 
  1. Pasa de guardar el programa en un fichero y ejecútalo directamente. Se le da el switch -e al intérprete para indicar que se debe ejecutar el script que viene a continuación (y ojo con las comillas)C:\PROGS\PERL>perl -e 'print "Passa, tio\n";'
Passa, tio
  1. Si estás en unix, se utiliza una sintaxis similar a los scripts del intérprete de comandos, es decir, guardar el fichero con una primera línea que indica dónde está el intérprete que va a ejecutar el resto del fichero. En el caso del perl, puede ser algo como #!/usr/local/bin/perl (los símbolos iniciales se suelen denominar pound-bang, o almohadilla-admiración, en román paladino). En todo caso, habrá que sustituirlo por el camino completo donde habita el intérprete de perl (si no se sabe, recurre al comando de unix which (como hemos visto antes), o, por último, al operador de tu sistema; ahora, que si tú mismo eres el operador de tu sistema y no sabes como hacerlo, tienes un problema, tío... Bueno, siempre te queda la internet). Tras salvar el fichero, habrá que dar la ordenunix% chmod +x passa.pl para convertirlo en ejecutable; hecho eso, se puede ejecutar directamente escribiendo
unix% passa.pl

Passa, tio
 

4. En Windows95, hay al menos dos formas de hacerloTM (suponiendo, claro está, que le pille de buenas y te deje hacer algo). Tras instalar la versión de PERL para Windows95 de la Internet, de la empresa ActiveWare, se crea un acceso directo a perl.exe y se copia al directorio \Windows\SendTo; esto hará que aparezca Perl dentro del menú enviar a, que es una de las opciones del menú que surge al pulsar con el botón derecho del ratón.
 

Otra forma es más específica para ficheros de tipo .pl, y es el mismo mecanismo que se usa para asignar iconos y programas ejecutables a los ficheros no ejecutables: en la carpeta MiPC, pasar a VerOpciones y pulsar sobre la pestaña Tipos de Archivo: Se crea un nuevo tipo que sean "Programas en PERL", y se le pueden asignar acciones como edición o ejecución; de esta forma, con sólo pulsar dos veces sobre el icono, se ejecuta el programa.
 

Para editar un programa en perl, lo más adecuado es conseguir un editor para programadores, porque el lenguaje no incluye un entorno integrado de programación. Lo más parecido a tal entorno integrado, tanto en Win95/NT como en UNIX, es el emacs, puesto que tiene un modo perl de edición, que tiene en cuenta indentaciones y otras particularidades de su sintaxis, como el emparejamiento de paréntesis y llaves y los comentarios, y asigna diferente color a las palabras dependiendo del contexto. Otra opción con unos pocos menos megas puede ser cualquier editor de programador con diferentes opciones dependiendo de la extensión del programa, como el Programmer´s File Editor.

4. Más difícil todavía
 

Ya que hemos hecho nuestro primer programa, vamos a por el segundo. Supongamos que somos un político corrupto cualquiera, y que, dado el alto número de transacciones diarias por las cuales cobramos comisiones ilegales, decidimos escribir un programa en perl que nos resuelva la difícil papeleta de calcularlas sobre la marcha. Ni cortos ni perezosos, nos puede servir lo siguiente:

print "Valor del inmueble? ";

$valor = <STDIN>;

$comision = $valor * 0.25;

print "Comision = $comision\n";

que da el resultado siguienteC:\PROGS\PERL>perl comile.pl

Valor del inmueble 1000000 Escrito por el usuario

Comision = 250000

 
 

En este ejemplo hay algunos elementos más de perl. Como se ve, las variables se indican con un símbolo de $ (sumamente indicado para el ejemplo); en este caso se trata de variables escalares, que contienen un valor numérico o una cadena; a perl le da exactamente igual que se trate de uno o de otro, la transformación se hace automáticamente. Y no hace falta ninguna declaración de variables; cada variable se declara en el momento que se usa, y si no se ha usado anteriormente, aparece automáticamente con el valor 0 o ´´ (cadena nula). Las variables en perl tienen todas visibilidad global.
 

El programa pide además, la intervención del usuario, leyendo en una variable lo que éste teclea. Para ello se utiliza un filehandle, o puntero a un fichero abierto (el equivalente a un FILE* en C), que está disponible en todos los programas, la entrada estándar o stdin (al igual que también existe la salida estándar o stdout, es decir, que es lo mismo print STDOUT que print). El nombre de este fichero no está precedido por ningún símbolo de $ para indicar que se trata de un filehandle, o una variable que representa un fichero. Los angle brackets, o paréntesis angulares, indican que se lee del fichero una línea completa, hasta que el que teclea pulsa .
 

Por último, se está utilizando la operación denominada interpolación de variables. Una variable incluida dentro de una cadena entre comillas dobles será sustituida por su valor (no en el caso de tratarse de comillas simples).
 
 

5. Dando vueltas sobre lo mismo
 

Cuando, tras las elecciones, nuestro político corrupto sale reelegido por méritos propios, viene inmediatamente la recuperación económica y con ello el boom inmobiliario (favorecido por la recalificación en terrenos construibles de los parques de la ciudad). Tantas comisiones tiene que calcular, que decide escribir un programa que lo haga continuamente, en vez de tener que ejecutar el programa anterior cada vez. Decide además guardar detalles de todo en un fichero, que llevará en un disquete escondido en el collar del perro, para que, en caso de que lo investigue una comisión del Congreso, pueda tirar de la manta tecnológicamente. Saldría entonces algo como lo incluido en el cuadro 1.

En este segundo programa, que llamaremos clientes.pl, se introducen algunos conceptos nuevos más. Para empezar, se utiliza la orden open para abrir un fichero; a esta orden se le dan dos parámetros, el filehandle por el cual nos vamos a referir al fichero en lo sucesivo, y una cadena que incluye el nombre del fichero y la forma de manejar ese fichero. En este caso, se usa la expresión ">clientes", que indica que se va a abrir para escritura. Otras opciones son las que aparecen en la tabla I. Si no se ponenada, se supone que el fichero se abre para lectura. Y esto es fuente de continuas confusiones.

A continuación se comienza un bucle con la orden while, que se ejecutará mientras la expresión entre paréntesis sea cierta, o sea, en este caso, en principio, siempre. Los valores "verdaderos" se indican en perl con un número distinto de 0 o una cadena no nula. Tras while va siempre un bloque, que se comienza y termina con llaves. Dado que es un bloque, y no la alternativa orden|bloque (como sucede, por ejemplo, en el lenguaje C), se tendrán que utilizar siempre las llaves, aunque se trate de un solo comando. También se podría sustituir esta línea por until(0) {que tendría exactamente el mismo significado (recuerda, Hay Más De Una Forma de Hacerlo). O por dos o tres formas más, al menos.
 

En la línea 4 se hacen dos cosas juntas: se asigna a una variable lo que introduce el usuario, y a esta variable se le elimina el último carácter (chop, que significa trocear, como en el chopped). Esto es necesario porque, a diferencia de otros lenguajes, perl incluye el retorno de carro en la cadena que lee. Lo eliminamos entonces para que quede todo bonito.
 

En la línea 5 se utiliza una construcción típica de perl <comando> <condicional> <condición>. En este caso, se sale del bucle (last) en caso de que lo que se haya leido sea la cadena vacía (recordad que previamente hemos eliminado el retorno de carro). Esta línea se podía haber sustituido por la siguientelast unless $paganini;que tiene exactamente el mismo significado (unless significa a menos que); en general, una sentencia en la que se utiliza if con una expresión verdadera se puede sustituir por otra en la que se utiliza unless con la misma expresión negada. Otras expresiones que regulan bucles son next, que ejecuta la iteración siguiente sin pasar por el resto de las órdenes del bucle, y redo, que vuelve a comenzar el bucle sin evaluar la condición.
 

En esta misma línea se usa el operador !, de negación. Este operador, como otros muchos, están sacados directamente del lenguaje C, en general, los operadores en perl son los mismos que en C, y además tienen la misma prioridad. Además, hay una serie de operadores específicos para cadenas alfanuméricas, sacados más bien del FORTRAN, y otros para ficheros que se verán más adelante.
 

En la línea 10 se escribe en el fichero MANTA; como se ve, simplemente se incluye el filehandle delante de la expresión que se va a escribir. Para terminar, después de concluir el bucle se cierra el fichero(2).
 

Estos primeros programas nos permiten ya tener una idea de cómo funciona perl. En realidad, salvo algunos asuntos menores de sintaxis de variables y su declaración, es muy similar al C: por eso siempre, la primera aproximación a un programa será hacerlo como uno lo haría en C, para luego, cuando se tenga mayor dominio del lenguaje perl, hacerlo de forma más perlera.
 

6. Ahorrando energías
 

Como nuestro político corrupto, por su experiencia en el ramo de la construcción, ha sido nombrado delegado de Obras Públicas, tiene que pasar la mayor parte del tiempo visitando carreteras, caminos, puentes y veredas. Nada mejor, pues, que comprar un portátil Pentium MMX para ir introduciendo las mordidas en el propio lugar donde se originen. El problema es el limitado rango de las baterías, que hacen que si hay que viajar de Albacete a Cuenca, se gaste la batería a la altura de Motilla del Palancar. Para ahorrar energía, decide modificar su programa de forma que escriba menos en el disco; es decir, que cargue todos los datos en memoria, y los guarde sólo cuando termine el bucle. Además, para no poner la excusa a la comisión del Congreso (que ya pide Anguita a voces) de que no se acuerda de nada, decide grabar también las fechas y horas de los sucesos. Escribe, con su pericia creciente en perl, el programa memoria.pl (cuadro 2).

En esta pequeña modificación del programa anterior, y en la línea 8, se introduce una nueva estructura de perl: el array o lista. Un array se indica con el símbolo @ (arroba), aunque a cada elemento de un array nos referiremos con la notación $mem[0], ya que es un escalar. En general, tanto los operadores como las funciones en perl se comportan de forma diferente si se trata de un entorno escalar o si se trata de un entorno de lista o vectorial. En este caso, el operador localtime devuelve una lista compuesta por los segundos, minutos y demas, tomándolo de la función time que devuelve el número de segundos transcurridos desde hace mucho tiempo (1970, para ser exactos). Además, estamos utilizando la lista @zape para contener el resto de los elementos de la hora, que no nos interesan (serían el día de la semana y cosas así). Por supuesto, también se podría haber hecho de otra forma, mucho más larga@fecha = localtime(time);

$seg = $fecha[0];

...Las matrices empiezan en perl en 0 habitualmente, como sucede en C; pero esto se puede cambiar (usando la variable global $[).
 

En la línea 11 hay un nuevo operador, push. Este operador hace precisamente eso, achuchar un escalar o una lista al principio de otra lista (recordad que lista y array son prácticamente sinónimos). En este caso, estamos metiendo la cadena que hemos creado, $zipi, al principio de la matriz @mem (que, por cierto, tampoco hemos tenido que declarar ni dimensionar). Si queremos eliminar el primer componente de una matriz, se hace con el operador obvio, $cadena = pop(@mem);

En la línea 15 aparece una nueva orden de control de bucle: foreach, para cada, que repite el cuerpo del bucle para cada uno de los elementos de la lista que hay contenida entre paréntesis. Se puede abreviar por for, aunque también se puede comportar como lo hace en C. ¿Y cuál es la lista a la que se aplica? La que hemos creado anteriormente, pero por orden (de ahí el sort). En este caso la ordenará por orden alfabético, saliendo algo como estoPepe Illo 4750000 13:19 24/3

Per Illan 4749997.5 13:19 24/3

Sin embargo, dentro del bucle no parece que pase nada; y, ¿dónde diablos se ha metido la variable de bucle?. Lo que ocurre es que en estos casos perl tiene una variable por defecto, $_, que es la variable de bucle por defecto y sobre la que actúan los operadores y funciones también por defecto. Es decir, que el bucle anterior equivaldría aforeach $_ (sort @mem) {

print MANTA $_;

}
 

Y aunque sé que ya estáis esperando que lo diga, se puede hacer de otra forma, esta vez menos perlística; utilizando bucles normales y corrientesfor ($i = 0; $i<=$#mem, $i++) {

print MANTA $mem[$i];

}si bien en este caso el fichero de salida no estará ordenado. En este caso se utiliza la construcción $#<nombre de matriz>, que devuelve el último índice existente para esa matriz. Ojo, se trata del último índice, no del número de elementos de la matriz; por eso los bucles en perl usan un <= para terminar.
 

 

7. Recordando, que es gerundio
 

Al final del día, nuestro político corrupto reflexiona. ¿De qué sirve tanto trabajo, sin una buena contabilidad? (Además, tiene que presentarle las cuentas al señor X a fin de mes). Idea, pues, el programa que aparece en el cuadro 3.

Este programa, aplicado sobre el fichero clientes.mas, mostrado en el recuadro IV (resultado de una incursión en la construcción de diversas viviendas para los cuerpos de seguridad del estado y sus departamentos de investigación y desarrollo, así como otros procedentes del mundo de la enseñanza) da el siguiente resultado

C:\PROGS\PERL>perl totales.pl clientes.mas

Trinque total del dia 3-24 = 598454.75

Trinque total del dia 4-25 = 1100987

Trinque total del dia 4-26 = 487775
 

Este programa, primer ejemplo de lectura de ficheros, empieza con una advertencia: "muere si no me metes un fichero como argumento". La orden die termina el programa con un mensaje; mientras que el condicional que lo sigue comprueba que exista un argumento al programa; la matriz @ARGV contiene los argumentos pasados al programa; de forma que $#ARGV dará el índice del último argumento, o sea que si es -1, indicará que no se ha pasado ningún argumento. Y otra forma de hacerlo sería

die "Sin argumento me lamento\n" if $#ARGV < 0;

El siguiente bucle, que comienza en la línea 2, tiene una extraña condición para que el bucle siga; sólo los dos ángulos enfrentados. Teóricamente, debería de haber un filehandle dentro de esos ángulos, pero en este caso, se está tomando el fichero por defecto, que es el fichero que se introduce como argumento; en caso de que no se hubiera introducido ninguno tomaría entrada estándar, es decir, que habría que introducirle cada línea mediante teclado. A la vez, y como se ha visto, esa orden toma una línea del fichero y la deposita en la variable por defecto, aunque, ojo, no le quita el retorno de carro final.
 

Sobre la variable por defecto actúa la orden split (una de las más usadas en perl), dividiéndola en una serie de cadenas separas por espacios y depositando cada una de esas cadenas en un elemento de la matriz @linea. Y dese cuenta el venerado público de con qué facilidad hemos hecho algo que requeriría al menos 10 líneas de cualquier otro lenguaje. No hay que dimensionar matrices, no hay que recorrer la cadena caracter por caracter... ¡Nada!(3). Perdón, me he dejado llevar por el entusiasmo.
 

La fecha es, en todos los casos, la última cadena de la línea; es decir, que será el último elemento de la matriz (cuyo subíndice es siempre $#<nombre-matriz>), pero a su vez tendremos que dividirlo, esta vez por la barra de separación, para luego poder poner el mes delante y que salga todo bellamente ordenado por meses en vez de por días.
 

Esta cadena con la fecha, más la pasta, que está siempre 2 posiciones más a la izquierda (independientemente de la longitud de los nombres), se utiliza en la línea 7 en una matriz asociativa. Esta es otra de las características más potentes del perl, se pueden usar matrices cuyo índice es una cadena cualquiera, no sólo números enteros positivos. Estas matrices asociativas encierran sus índices o claves entre llaves (¿os dáis cuenta del sutil mnemónico?). En esa línea, se le añade al componente de la matriz indexado por la fecha la pasta de la entrada correspondiente. Así, hasta que se termina el fichero de entrada.
 

Para imprimir el informe, tres cuartos de lo mismo que en el programa anterior, salvo que en este caso, una matriz asociativa completa se indica con %, en vez de la arroba.
 

8. Purgando los pecados.
 

No es precisamente lo que tenía en mente nuestro político cuando decidió ver como aumentaba su capital con cada aportación de su clientela; o sea, que hacer un programilla en perl para calcular sumas parciales, y sumas totales, no estaría mal; quizás incluso podía añadirle que sonara una campanita cada vez que sume cien talegos más. Así pues, hace el programa purga.pl, para aplicarlo sobre el fichero clientes.mas del cuadro IV:while(<>) {

split(/ /);

print "Total parcial ", $total+=$_[1], ",\n";

}

que divide cada línea en sus componentes separados por espacios, y suma el segundo componente, índice 1 de la matriz. Pero cual no sería su desagradable sorpresa al ver que el dinero no aumentaba, sino que daba este resultado:C:\MyFiles\Textos\docencia\PROGS>perl purga.pl clientes.mas

perl purga.pl clientes.mas
 

Total parcial 150000,

Total parcial 474999.75,

Total parcial 598454.75,

Total parcial 1154120.75,

Total parcial 1699441.75,

Total parcial 1699441.75,

Total parcial 1699441.75,

¡Diablos! 3 clientes no habían aportado su granito de arena a la grandeza del partido. ¿Cómo podía ser eso? Por mucho que lo ejecutaba no funcionaba de otra manera (como es natural), así que tuvo que aprender a manejar algo llamado purgante o purgador o algo así. Para ejecutarlo, hay que añadir la opción -d en la línea de comandos o la primera línea del script en UNIX:perl -d todebug.pl clientes.mas
 
 

Loading DB routines from perl5db.pl patch level 0.95

Emacs support available.

Enter h or `h h' for help.

main::(todebug.pl:1): while(<>) {

DB<1> Este es el depurador o debugger de perl, con el cual se puede hacer lo habitual en cualquier depurador, aunque quizás de una forma un poco menos amistosa. Por ejemplo, se pueden poner puntos de ruptura con b (breakpoint), para examinar qué es lo que va mal: DB<1> b 3Al ejecutar el programa con c (continuar) o r (ejecutar), el programa parará en esa línea: DB<2> r

main::(todebug.pl:3): print "Total parcial ", $total+=$_[1], ",\n";imprimiendo la línea de que se trata. Entonces podemos examinar variables, matrices o lo que se tercie, con la orden p, de print o imprimir. Incluso, con la orden <, nos ejecuta un comando antes de pararse, por ejemplo: DB<3> < print $_[1]Al llegar a las líneas problemáticas, nos encontramos quemain::(todebug.pl:3): print "Total parcial ", $total+=$_[1], ",\n";

Bacterio DB<5> c

Total parcial 1699441.75,

main::(todebug.pl:3): print "Total parcial ", $total+=$_[1], ",\n";

DB<5> o sea, que en algunos casos, no se cumple que el segundo componente sea numérico: en un caso es una cadena, y en otro caso nada... Si nos fijamos en el fichero original, vemos que hay dos espacios tras Ofelia (será para hacerle sitio), con lo cual perl, con toda su ilusión, divide la línea y no mete nada entre ellas. En realidad, esa línea es bastante rara, porque además aparece dos veces la cantidad, pero es igual. En cualquier caso, eso nos dice que en vez de usar el segundo componente desde el principio, debemos de usar el cuarto desde el final, es decir, $_[$#-3]. Con lo cual el programa funciona perfectamente, y nuestro político lo ha purgado.
 

En realidad, el debugger de perl es bastante potente, a pesar de su presencia espartana, sobre todo porque incluye un intérprete y puede ejecutar sobre la marcha cualquier expresión. Incluso es aconsejable ejecutar un programa la primera vez desde este depurador, para corregir sobre la marcha cualquier problema.
 

El resto de las órdenes del depurador, que incluyen puntos de ruptura condicional, ejecución paso a paso (con s o simplemente dándole a enter), se consiguen con el comando de ayuda, h.
 

9. Regularizando la situación
 

Una forma todavía más directa de trabajar con estos ficheros de texto que tienen una estructura regular (que son la mayoría), es usar expresiones regulares. Una expresión regular es una forma de expresar gramaticalmente la estructura de cualquier cadena alfanumérica. Por ejemplo, una cadena compuesta por una letra inicial, con una letra o un número a continuación se podría expresar de la forma siguiente

letra {letra|número}*

donde | expresa una alternativa y * indica que puede aparecer 0 o más veces. Pues bien, estas expresiones regulares se utilizan enormemente en perl (de hecho ya hemos usado una, aunque compuesta por un solo caracter, en la línea 4 del programa anterior).
 

La expresión regular más simple es la propia cadena con la que se quiere comparar; es decir, una cadena coincidirá consigo misma. Las expresiones regulares en perl siempre van encerradas entre //, y si no se indica nada, comparará la expresión regular con la variable por defecto, $_, es decir, que /pepe/ será cierto si $_ contiene íntegra la cadena pepe. El programa$_ = "pepeillo"; print "si" if /pepe/;imprimirá si.
 

Las expresiones regulares usan símbolos convencionales para referirse a grupos de caracteres y otros para repetición o señales de puntuación. En algunos casos, si el símbolo significa algo dentro de la expresión regular (o en perl), se precede por \ (escape). Por ejemplo, . significa "cualquier carácter" dentro de una expresión regular; luego para referirnos al ., usaremos \.. También se usa \/,\? y \*, por ejemplo. Otras expresiones más complicadas incluirían repeticiones de símbolos; por ejemplo, \w+ casaría con cualquier palabra (un carácter alfanumérico repetido uno o más veces).

Para agrupar símbolos se usa el paréntesis, que además sirve para indicarle a perl que con lo que hay en su interior se va a hacer algo. Para empezar, se asigna a la variable $&; pero además, podemos asignar a otra variable el resultado de la comparación; en general, una comparación con una expresión regular que incluya paréntesis devuelve una lista con todo lo que coincida con el contenido de los paréntesis$zipi = "Pepeillo Gonzalez MacKenzie 8000";

@parejas = ($zipi =~ /(\D+) (\d+)/);(el nombre de la variable y el símbolo =~ se pueden suprimir si la comparación se hace sobre la variable por defecto $_); @parejas contendrá ("Pepeillo Gonzalez MacKenzie",8000), ya que la primera expresión regular indica "uno o más caracteres no numéricos", mientras que la segunda representa "uno o más caracteres numéricos".
 

Por ejemplo, el fichero de paganinis usado en ejemplos anteriores anterior tiene la estructura siguiente: una o más palabras, separadas por un espacio, una cantidad (números y puntos), una hora (números y dos puntos) y una fecha (números y /). Esto se dice en PERL mediante la expresión siguiente(\D+) (\d+\.?\d+) (\S+) (\d+)\/(\d+)

que puede parecer un poco críptica (y en realidad lo es), pero cuyo significado se puede resolver mirando el cuadro Tabla IV. Con esta modificación, el bucle central del programa del cuadro 4 se queda reducido a

while(<>) {

($paganini, $pasta, $hora, $dia, $mes)

= /(\D+) (\d+\.?\d+) (\S+) (\d+)\/(\d+)/;

$totalDia{"$mes-$dia"}+=$pasta;

}
 

Más compacidad no se puede pedir. Además, ya de camino, nos vamos ahorrando algunas variables. En la primera línea del interior del bucle se asigna la parte de la cadena que coincide con lo descrito en el interior de los paréntesis a cada una de las variables, es decir, divide la línea en cinco campos, cada uno de los cuales tiene un tipo diferente. Hay cinco pares de paréntesis, para cinco variables. Veamos cada expresión regular por partes.
 

La primera expresión regular es \D+. Como se ve en el cuadro Tabla IV, \D coincide con todo lo que no sea numérico, y en particular letras y espacios. Este campo es problemático, porque puede incluir una o varias palabras; sin embargo, sabemos que el siguiente campo comienza por un número; por tanto, incluiremos en este campo todo lo que haya hasta que encontremos un número. Se puede insertar el código siguiente print $paganini; para ver qué se incluye dentro de ese campo.
 

La siguiente expresión regular, (\d+\.?\d+), no describe otra cosa que un número real en notación de coma flotante, es decir, una o más cifras (\d+), seguidas o no por un punto (\.?, no olvidar que el punto tiene significado especial en las expresiones regulares), que a su vez debe de estar seguido por una o más cifras (\d+). Esta expresión regular coincide además con aquellos números sin punto enmedio.
 

Y la siguiente \S+ coincide con la hora, aunque quizás podría coincidir con cualquier cosa, simplemente coge todo lo que haya entre los dos espacios. En realidad, si pusiéramos esta expresión regular como(\S+) (\S+) (\S+) (\S+) funcionaría perfectamente.
 

Y para terminar,(\d+)\/(\d+)coincide con el día y el mes, que están separados por una barra diagonal.
 

Con estas expresiones regulares se pueden construir programas superpotentes, que convierten casi cualquier texto en casi cualquier otro. Incluso, si uno se atreve (nuestro político corrupto no creemos que se atreva) un analizador sintáctico de un lenguaje de alto nivel, aunque sea simplificado. Otra utilidad es hacer filtros de correo electrónico, o de noticias de USENET (de hecho, el killfile o fichero que incluye todas las expresiones regulares de mensajes que deben de ser borrados, usa expresiones regulares). También se pueden usar expresiones regulares en archie y en la Web; el problema es que la mayoría de las veces usan convenciones diferentes, pero en cualquier caso, nunca viene mal saberlas.
 

El político corrupto decide no contarle al señor X todo el dinero obtenido (por no mencionar a Hacienda), y poniéndose a trabajar sobre los ficheros generados anteriormente (como cliente.mas), elabora el siguiente programilla (changec.pl)#!/usr/local/bin/perl -p

s/(\d+\.?\d+)/$&*0.85/e;

que ni siquiera pongo en un cuadro aparte, porque no merece la pena.
 

En este programa se introducen novedades desde la primera línea. Para empezar, se usa perl con un switch en la línea de comandos, -p. Este switch indica que alredededor de lo que hay debajo hay que sobreentender el siguiente buclewhile(<>) {

s/(\d+\.?\d+)/$&*0.85/e;

print;

}

; es decir, un bucle que abre y lee del fichero que se pasa en la línea de comandos, ejecuta las órdenes encontradas en el fichero, y al final imprime la variable por defecto. Si el switch fuera -n en vez de -p no imprimiría.
 

También se incluye la nueva orden s///[switches]tomada, como otras órdenes, del editor de UNIX sed. En concreto, esta orden lo que hace es sustituir la expresión regular encontrada entre los primeros // por la expresión en los segundos. En este caso actúa sobre la variable por defecto, pero en general$zipi="pepeillo";

$zipi=~ s/p/q/g;daría "qeqeillo". El switch e que aparece al final de la orden en el programa indica que se tiene que evaluar la expresión que aparece en la segunda parte de la orden; el g que aparece aquí indica que se tiene que hacer una sustitución global, es decir, que tienen que sustituirlo todas las veces que aparezca en la primera expresión, no solo una vez.
 

Por último, la variable $& contiene la cadena que coincida con la expresión regular en la primera parte de la orden; los paréntesis indican qué parte de la expresión hay que asignar a tal variable. Si hay varios paréntesis, las cadenas correspondientes serán asignadas a las "variables" \1, \2 (dentro de la orden s), o $1, $2... fuera de la expresión (estas variables sólo funcionan un ratito, asi que no van a estar disponibles hasta que uno quiera; en caso de duda, es mejor asignarlas a otra variable.
 

 

10. Presentando lo impresentable
 

Tanto trabajo está acabando con la salud de nuestro político. La campaña electoral está cerca, tiene que inaugurar unas cuantas obras inacabadas todavía, y decide descargarse de un poco de trabajo. Para ello contrata a un militante de base como experto de Informática con cargo a los fondos reservados. Este se encuentra un trabajo ingente, y para empezar decide facilitar un poco su labor escribiendo una utilidad que le permita sacar directorios un poco más monos. Y, ni corto ni perezoso ("Este chico llegará lejos", comenta nuestro político) escribe el programa dir.pl,que aparece en el recuadro.

 

En este programa ya estamos hablando de palabras mayores. Para empezar, es un poco más largo. Para continuar, es sólo para MS-DOS o Win95, pero se puede adaptar más o menos fácilmente a cualquier otro sistema operativo cambiando el separador de directorios contenido en la variable $dirSep (: para el Mac, / para UNIX). El programa saca un listado del directorio, ordenado en orden alfabético, resaltando los directorios con un tipo de letra más brillante, y los ficheros en video inverso.
 

Se le llama de la forma siguiente

C:\> perl dir.pl <nombre del directorio>

Si no se da ningún nombre de directorio, toma el directorio raíz; para el directorio actual hay que introducir perl dir.pl ..
 

Veremos primero el truco de sacar las cosas en diferentes tonos, y que se expresan en el programa con extraños signos (para obtener la flechica p'atrás, se pulsa control-P, y sin dejar de pulsarlo, control-[; difícil, pero es así). El programa usa secuencias de escape ANSI, que son órdenes estándar para trabajar con terminales; son tanto aplicables a terminales UNIX (por ejemplo, los tipo vt-100 o ansi), como MS-DOS; en este último habrá que añadir la línea

device=c:\dos\ansi.sysal fichero config.sys. Con estas secuencias de escape se puede imprimir en diferentes lugares de la pantalla, borrar líneas o la pantalla completa, y cambiar, como aquí, los atributos de partes de la pantalla, todo ello de forma más o menos independiente del sistema. Es algo así como el interfaz gráfico de usuario de los pobres.
 

El siguiente concepto que introduce este programa es el de subrutinas. Las subrutinas en perl se pueden declarar en cualquier parte del programa, al principio o al final. Para declararlas, simplemente se precede el nombre de la subrutina con la palabra sub y se le sigue del bloque de instrucciones que se van a ejecutar. Pueden no utilizar parámetros (como borra y normal). En este caso se llaman precediéndo el nombre con el símbolo &, o bien escribiendo do borra. También se puede llamar a una subrutina con parámetros, en cuyo caso forman todos parte de la matriz @_, como se ve por ejemplo en la subrutina negrita. Los elementos de esta matriz son, como es natural, $_[0], $_[1] y así sucesivamente, aunque se pueden asignar todos de una vez a parámetros locales de la subrutina, de esta formalocal($zipi, $zape) = @_;En esta sentencia se declaran simultáneamente $zipi y $zape como variables locales y se les asignan los dos primeros parámetros que se le han pasado a la subrutina. Si no se declararan como local, las variables serían visibles por todo el programa, como sucede en el resto de las variables en perl.
 

La lectura del directorio se hace a partir de la línea 31; para eso utiliza funciones similares a las que hay en c o Pascal; una funcion, opendir, "abre" el directorio, y las otras van leyendo secuencialmente los nombres de todos los ficheros de ese directorio y metiéndolos en la matriz @ficheros.

Luego, a partir de la línea 49, y utilizando los operadores -f y -d, que comprueban si el fichero cuyo nombre viene después es un fichero normal o un directorio, se va imprimiendo con los atributos correspondientes. Estos son un ejemplo de los operadores de fichero que se utilizan mucho en perl (ver Tabla VI), y que vienen heredados de comandos similares en los shell de unix (como csh y bash), nosotros tenemos la suerte de poder usarlos en ms-dos también. Otros operadores figuran en la Tabla VI.
 

11. Decodificando
 

En realidad, la contratación de este nuevo genio de la informática, al que ya le está buscando también colocación en la empresa que controla los ordenadores de la campaña electoral, le ha dejando algún tiempo libre a nuestro [censurado] político. Para emplear este tiempo libre, decide conectarse con eso que le ha dicho su amigo Borrell, que se llama internez o algo así; dentro de la internez, y de una parte especialmente yanqui llamada usanez o algo así, hay unos grupos de discusión de temas de máxima actualidad. En concreto, un grupo denominado alt.binaries.pictures.erotica.politics publica interesantes fotos de políticas de la oposición en actitud de debate (al menos eso es lo que le han contado).
 

Pero su gozo se encuentra en un pozo cuando sólo ve letras y más letras en los mensajes que le van llegando. Su informático residente le explica que vienen codificadas con un programa denominado uuencode, y que tiene que salvarlas cada una por separado y decodificarlas después, antes de verlas. Con la pericia que dan las ganas, escribe el siguiente programa, uudec.pl.

Este programa utiliza la orden unpack para decodificar cadenas, y las escribe en salida estándar. La descripción es incluso más corta que el programa. Los mensajes en ciertos foros de USENET vienen divididos en varias partes, cada una de las cuales contiene un pedazo de un fichero codificado con uuencode (puede ser un .GIF, un .WAV o cualquier otro). Para usar el programa, se salvan todas las partes del mensaje correlativamente en un fichero. El programa busca primero el nombre del fichero codificado, que aparece en una línea de la formabegin 644 nomfich.gif, abre un fichero con ese nombre, y va escribiendo en él las cadenas decodificadas.
 

Para llamar a este fichero, se escribe

unix% uudec.pl fichero.uuy se crea el fichero codificado, que luego podrá uno contemplar con su visor de GIFs preferido (o escuchar con la SoundBlaster).
 

En realidad, la orden unpack decodifica de formatos de codificación internos a cadenas; la orden pack hace exactamente lo contrario. La utilidad de todo esto se me escapa un poco, salvo que, por ejemplo, se quieran almacenar números reales de forma que pueda leerlos directamente un programa en C. En ese caso, se escribiría

print pack( "f", $zipi);Un número real escrito así se podría leer directamente con la orden read del C (pero, ojo, no con la scanf, que sirve solamente para ficheros de texto).
 
 

12. Todo bajo control
 

Cuando nuestro político corrupto descubre que UNIX tiene ya preprogramadas muchas de las funciones que le hacía el militante de base, lo despide y decide usar esos programas de UNIX desde, por supuesto, un programa en PERL. Por ejemplo, para contar las palabras que hay en un fichero basta con usar la orden wc de UNIX. Pero si se presenta en modo bonito, puede salir el siguiente programa, llamado wc.pl #!/usr/local/bin/perl

while (<*>) {

($zipi, $lineas, $palabras, $bytes, $nombref) =

split(/\s+/,`wc $_`);

print "El fichero $nombref tiene $palabras palabras y $lineas lineas\n";

}
 

La primera línea del bucle usa una bonita capacidad de perl, llamada globbing o globeo. Lo que hace es que, si se pone algún comodín de ficheros del tipo * repetirá el bucle para cada fichero que cumpla ese patrón, asignando el nombre del fichero a la variable por defecto. Ese nombre se le pasa al comando wc, usando ``, es decir, comillas invertidas, que ejecutan un comando en UNIX devolviendo su salida estándar. Esa orden completa agarra la salida del comando, del tipo

lineas palabras bytes nombre del fichero

y la parte en sus componentes. Como las diversas palabras están separadas por varios espacios o tabuladores, se una el patrón \s+. Al principio de la línea hay una serie de espacios, por lo que el primer componente devuelto no tendrá nada.
 

Hay otra forma diferente de controlar programas en UNIX, esta vez interactivamente, usando los denominados pipes o tuberías, que se suelen indicar con el carácter |. Por ejemplo, la orden open( FICHERO, "|pepe" ), abre una tubería a la orden pepe, que a partir de entonces ejecutará los comandos que reciba del programa en perl, ya que éste programa le suministrará la entrada estándar. Esto se puede usar de la siguiente forma (si se dispone del programa gnuplot)open ( GNU, "|gnuplot" );

print "Fichero a trazar? "; chop($fileN = <STDIN>);

print GNU "plot \"$fileN\" with lines 1\n";que abrirá una tubería al programa y le enviará la orden para trazar un fichero con líneas. Con este tipo de órdenes y con gnuplot, y con unas pocas ganas, se pueden hacer animaciones. Sin embargo, hay que tener en cuenta que sólo se podrá hacer con programas que acepten comandos por su entrada estándar.
 

13. Algunos consejos
 

Mucho cuidado con los espacios, con los puntos y comas y los demás caracteres que no están ahí de adorno

Un espacio entre el $ y el nombre de una variable, dará un error de sintaxis. O un espacio entre el # y el !, que dará un error extraño, ya que el shell interpretará el resto de la línea como un comentario. Y no olvidemos nunca el ; al final de las sentencias, ni las llaves alrededor de cualquier bloque, como por ejemplo en los bucles.
 

Sigue siempre la via perl

Aunque nos permita recurrir a lo que conocemos de otros lenguajes, perl permite hacer las cosas de forma mucho más compacta, elegante y a veces rápida. Por ejemplo, en vez de $zipi = $matriz[0];

$zape = $matriz[1]; en perl se puede hacer:($zipi, $zape) = @matriz; . O para dividir algo como "González McKenzie, Pepeillo", lo más rápido es ($Ap1, $Ap2, $Nombre) = /(\S+) (\S+), (\S+)/; (previa asignación a $_); en vez de utilizar split dos veces. O @fichero= <FILE>; en vez de usar un bucle para leer el fichero línea a línea. Los operadores de asignación de matrices, las variables asociativas y las expresiones regulares son fundamentales en casi cualquier programa perl; dominarlas es alcanzar el Zen del perl
 

Aprovéchate del depurador

Tiene órdenes bastante simples, y permite hacer muchas cosas; de hecho, todo lo que se puede hacer con perl. Aunque parezca un poco espartano, es la mejor opción para empezar a crear un programa.
 
 

14. Bibliografía.
 

perl viene con todo el material necesario para trabajar; en el caso de la versión 4, con una macro-página de manual en formato unix, y en el caso de la versión 5, con varias páginas de manual, e incluso páginas de manual en HTML. Hay, además varios libros publicados:

Programming perl, de Larry Wall y Randall L. Schwartz, O'Reilly and associates. Se le suelle llamar "The Camel Book" por el camello de la portada; se puede pedir directamente por e-mail a info@ora.com, o mediante la WWW a http://www.ora.com; o, por supuesto, en librerías que tengan libros técnicos en inglés.

Learning perl, de Randall L. Schwartz, O´Reilly and associates. El "Llama book"; se puede obtener de lal misma forma.

Teach yourself PERL in 21 Days, David Till, SAMS Publishing; un tocho considerable que me encontré en una librería al lado de la Facultad. Muy completo, pero no tan divertido como el "Camel Book".

Developing Applications in Perl, de Tom Christiansen, Byte, April 1994, p. 231-236.
 

Además, se han publicado tutoriales en diversas publicaciones, como UNIX World (Mayo-Julio 1990), y en la fenecida revista Click!, de marzo a junio de 1995 (una versión anterior de este mismo tutorial).
 

15. Recursos Internet

Lo principal: La metaFAQ de perl, disponible en ftp://kal-el.ugr.es/click/perl-meta-faq; y en http://www.khoros.unm.edu/staff/neilb/perl/metaFAQ/metaFAQ.html. Aquí se puede encontrar toda la información posterior, y la bibliografía.

Alguna cosilla también hay en http://www.perl.com, sitio central dedicado al PERL, con punteros a los sitios CPAN, o red de sitios con archivos relacionados con el PERL, código y librerías.

Hay unos cuantos grupos de usenet dedicado al perl; comp.lang.perl, tiene un tráfico de unos 100 mensajes al día, y normalmente los propios desarrolladores de perl, Larry, Tom Christiansen (que, por cierto, habla español) y Randy Schwartz contestan a tus dudas. Mucho tráfico, pero nada que no pueda salvar un buen killfile; otros grupos se dedican a anuncios comp.lang.perl.announce, y a una version de perl combinada con tcl/tk, el tkPerl, comp.lang.perl.tk.

Dónde conseguirlo: el sitio principal es ftp://ftp.cis.ufl.edu/pub/perl; aquí están todas las versiones, así como versiones compiladas para diversas máquinas.

Manual de referencia: escrito por Johan Vromans, está entre otros sitios en ftp://ftp.cs.ruu.nl/pub/DOC. Viene includo con el libro del camello; hay además tanto para la versión 4 como la 5.

perl para DOS: ftp://src.doc.ic.ac.uk/packages/ibmpc/simtel/perl; en el mismo sitio hay versiones para el Mac y el Amiga.

Camiseta perl : http://www.emoticon.com/emoticon/perl.html. Una compañía inglesa en la que se puede comprar una camiseta que tiene por delante el célebre camello, y por detrás tu dirección de e-mail entre otras más.

1. Aunque el comportamiento es ligeramente diferente en unix y msdos; mientras que este separa las líneas con un retorno de carro y linefeed, unix y el Mac usa solo un linefeed. En cualquier caso, \n se comporta de la misma forma en los dos.

2. Tener en cuenta que UNIX y Windows95/NT distinguen entre nombres en mayúsculas y minúculas, a diferencia de MS-DOS.

3. Aunque hay todavía una forma Más Fácil de Hacerlo