tutorial dividido en
capítulos. Descargar el tutorial completo, en .zip y el tutorial completo, en .tgz. También los ejemplos |
jmerelo@geneura.ugr.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.005, y posiblemente pronto, la 5.6) se ha convertido en
un lenguaje de propósito general, y una de las principales herramientas de un
buen internetero y de un buen webmaster.
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 solucionarlo.
Es un lenguaje que hereda estructuras principalmente de los intérpretes de
comandos de UNIX, especialmente elcsh
, y de otras utilidades
estándar, comoawk
ysed
. En realidad, puede hacer todo
lo que hacen cualquiera de ellos y todos ellos juntos, y 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 en realidad, el intérprete de PERL, como todos los intérpretes modernos, compila los programas antes de ejecutarlos. 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, en enero del 2000, la versión 5.005 es suficientemente estable como para usarla en cualquier plataforma. 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. La mayor parte de 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. Cuando son para la versión 5 de PERL, se indicará explícitamente con este signo v5.
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 simplemente
UNIX% which perl /usr/local/bin/perl
y 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 bajarte la última versión, consultar el apartado de enlaces
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. Existen además extensiones al Apache que permiten ejecutar directamente programas en PERL desde el servidor de HTTP.
Mediante una serie de módulos adicionales, tales como el DBD o el ODBC, PERL puede servir para acceder a bases de datos, desde BD gratuitas como MySQL hasta el Microsoft SQL server usando ODBC. Por supuesto, esto se puede combinar con un CGI para hacer aplicaciones tales como un carrito de la compra para un sitio web. Pero esto se deja como ejercicio para el lector.
Primero, hay que bajarse e instalar alguna versión de PERL. Hay versiones para casi todos los sisetemas operativos, o sea que no debería de ser complicado. Es conveniente consultar la sección de enlaces, para ver dónde podemos conseguirlo.
En todo caso, lo más probable es que si tienes una distribución de Linux más o menos decente, tal como RedHat , SuSE o Debian, venga ya incluido. Consulta los paquetes que tienes instalados (usando la herramienta correspondiente) o simplemente escribe:
bash$ which perl /usr/bin/perl
Si contesta algo así como lo anterior, es que está instalado.
En Windows, como siempre, es otro cantar. Sin embargo, hay una excelente versión de PERL para Windows, de la empresa ActiveState. No hay más que bajárselo, pulsar en el icono de SetUp correspondiente, y se lanza un procedimiento de instalación similar al de todos los programas. Desde ese momento, ya se puede usar desde la línea de comandos.
En MSDOS, basta descomprimirlo, y añadir al PATH el directorio donde se
encuentra perl.exe
.
En otros sistemas operativos, puede que no exista una versión binaria. Habrá que bajarse los fuentes de alguno de los sitios web existentes, y luego compilarlos usando los siguientes comandos (si es que es un SO parecido a UNIX:
sh Configure
make
make test
make install
Para más información, consultar el fichero INSTALL
o
perldoc INSTALL
que viene con los fuentes (malamente podrás usar
perldoc si no tienes instalado PERL, pero en fin...).
Escribir el archiconocido "Hola" en PERL no puede ser más fácil:
print "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
, que se suele
leer "escape-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.
passa.pl
. Se
puede ejecutar con
C:\PROGS\PERL>perl passa.pl
Passa, tio
Todo esto suponiendo que PERL esté en el PATH de
ejecución, claro está.
-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
#!/usr/local/bin/perl
, o #!/usr/bin/perl
en RedHat Linux, por ejemplo) (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
UNIXwhich
(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 orden
UNIX% chmod +x passa.pl
para convertirlo en ejecutable; hecho eso, se puede ejecutar
directamente escribiendo UNIX% passa.pl
Passa, tio
![]() |
![]() |
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..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 Ver - Opciones 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. Aunque, por supuesto, si puedes
conseguirte el emacs para Windows, es con
diferencia lo mejor.
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 (comile.pl):
|
que da el resultado siguiente
C:\PROGS\PERL>perl comile.pl
Valor del inmueble 1000000 [Escrito por el usuario]
Comision = 250000
A partir de este momento, indicaremos en rojo
las palabras clave de PERL,
en verde
las variables de
PERL y en azul
las órdenes
de control.
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, si no se dice lo contrario.
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
(estandard input); de la misma manera, también existe la salida estándar o
stdout
, es decir, que es lo mismoprint STDOUT
queprint
). El nombre de esta variable 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 un retorno de carro.
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).
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 listado siguiente:
|
En este segundo
programa, que llamaremos manta.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 1. Si no se pone
nada, se supone que el fichero se abre para lectura. Y esto es fuente de
continuas confusiones.
Como siempre, hay Más de Una Forma de Hacerlo TM, aunque no
necesarimente más corta. Se puede meter el nombre del fichero en una variable
tal como $MANTA
, y abrirlo de la forma siguiente:
$MANTA='>prueba';
open MANTA;
Es decir, cuando no se pone una cadena detrás del filehandle, se usa
el contenido de una variable que tenga el mismo nombre. Véase también que en
este caso no he usado paréntesis detrás de open; son opcionales. Hay muchas más
cosas sobre open leyendo el tutorial incluido en la documentación, se accede a
él escribiendo perldoc perlopentut
o man perlopentut
.
|
A continuación se comienza un bucle con la ordenwhile
, 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 tal como uno lo haría en C, para luego, cuando se tenga mayor dominio del lenguaje PERL, hacerlo de forma más perlera.
El político corrupto se da cuenta de que tiene en un fichero guardadas una
serie de sobornos anteriores en un fichero; pero no le puso nombre; así que,
antes de que se le vayan las cosas de la memoria y no diga nada salvo en
presencia de su abogado, decide hacer un programa que le vaya preguntando
cantidad por cantidad quién fue la que se la dió. Y decide hacer el siguiente
programa (recuerda.pl
):
|
Este programa utiliza las mismas estructuras que el anterior: ficheros de
entrada y salida, y un bucle. Sin embargo, se usan de forma diferente. Para
empezar, la sintaxis de apertura de los ficheros en las líneas 3 y 4 es
diferente: se evitan los paréntesis, casi 4 bytes, que en una memoria de 512
megabytes, quieras que no, es un ahorro. El fichero cantidades
se
abre para lectura, por lo que no es necesario poner el símbolo <, y el
fichero salida
se abre para escritura.
Lo que sigue es bastante típico de la lectura de ficheros en PERL: se lee una
línea del fichero abierto (usando la construcción <>
, se
asigna a la variable $line
. Además, la línea está dentro de una
condición de continuación de un bucle; efectivamente, $line
será la
cadena vacía, y por lo tanto falso, cuando el fichero deje de tener
elementos.
El interior del bucle no aporta nada nuevo: simplemente se le eliminan los
retornos de carro a lo que se lee usando chop
, y se combina la
entrada con lo que introduce el usuario para escribirlo en la salida.
Finalmente, en las líneas 11 y 12, se cierran los ficheros.
|
$_
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 AMD K6-2 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
(listado 3).
|
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
$[
). Hay que tener cuidadito con la variable $anho
,
que devuelve, al contrario de lo que se pudiera pensar, el número de años
transcurridos desde 1900. Eso da lugar a todo tipo de pequeños efectos 2000, que
todavía se ven por algún sitio Web. Por eso, en la línea 10, se le suma 1900,
para que dé el año actual con cuatro cifras. Tal como en el lenguaje C,
$anho+=1900
equivale a $anho=$anho+1900
;
En la línea 12 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 16 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 esto
Uno 750 12:0 31/1/2000
otro 1402654 12:0 31/1/2000
otro mas 4020267 12:1 31/1/2000
y otro mas todavia 4040.25 12:1 31/1/2000
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 a
foreach $_ (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 corrientes
for ($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.
|
split
, matrices asociativas y matrices
bidimensionalesAl final del día, nuestro político corrupto reflexiona. ¿De qué sirve tanto trabajo, sin una buena contabilidad consolidada? (Además, tiene que presentarle las cuentas al señor X a fin de mes). Idea, pues, el programa que aparece en el listado 4.
|
Este programa, aplicado sobre el fichero clientes.mas
,
(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 (o algo parecido)
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 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 al menos un
argumento para el 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;
O incluso
$ARGV[0] || die "Te has quedado sin argumentos\n";
que mira lo que hay a la izquierda del ||
(que es el "o"
lógico), y si es cierto, ejecuta lo que hay a la derecha. Recuerda, en
PERL hay más bla, bla.
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 (como
se ha visto en un ejemplo anterior), 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. Hay que tener en
cuenta que los paréntesis angulares sin argumento extraen elementos del array
@ARGV
usando pop
, y abren un fichero con ese nombre; o
sea que si hubiera varios nombres de fichero en la línea de comandos, los iría
abriendo uno por uno y disminuyendo consecuentemente el tamaño de
@ARGV
; conclusión, que si necesitas @ARGV
para algo,
mejor que lo guardes antes de meterte en un bucle de esta guisa.
Sobre la variable por defecto actúa la orden split
(una de las
cosas más usadas en PERL), dividiéndola en una serie de cadenas
separadas 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.
|
[v5]A partir de la versión 5 de perl, se pueden usar matrices bidimensionales, mediante un mecanismo que se llama referencias; este mecanismo no nos interesa ahora mismo, pero sí como podemos usarlo en nuestros programas, tales como el siguiente (totales-v5.pl):
|
Este programa es bastante parecido al anterior, salvo que, en vez de almacenarse los resultados en un array asociativo, se almacenan en un array bidimensional; la primera dimensión es el mes, y la segunda representa el día del mes. La línea 2 cambia simplemente para indicar otra forma de detectar si hay un fichero en la línea de comandos o imprimir un mensaje de error.
La línea 7 es la que usa una matriz bidimensional. Hasta aquí, todo normal;
lo único relevante es que no hay que dimensionarla. Como primer índice de la
matriz se usa el elemento 1 de $linea
, es decir, el mes, y como
segundo índice el primer elemento (el 0), es decir, el día.
Donde sí se nota un cambio es a partir de la línea 10; el bucle lo hacemos
sobre una matriz normal y corriente. El problema es que, tal como sucede en las
matrices bidimensionales en C, en PERL las matrices
bidimensionales son en realidad una matriz de matrices, o mejor una matriz de
referencias. Por eso, en la línea 11 lo que se hace es dereferenciar el elemento
correspondiente y convertirlo en una matriz que podamos usar, la matriz
@dias
. En la línea 13, usando interpolación, se imprimen todos los
elementos que son no nulos (if $totalDia[$i][$j]
) la mayoría serán
nulos , y los dejamos sin imprimir. [/v5]
|
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
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:\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 al llamar al PERL o la
primera línea del script en UNIX:
perl -d purga.pl clientes.mas Loading DB routines from perl5db.pl patch level 0.95 Emacs support available. Enter h or `h h' for help. main::(purga.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 Supuestamente, se puede usar desde emacs, aunque yo no he
visto ninguna ventaja en ello. Sin embargo, desde xemacs
es mucho
más fácil de usar; se accede directamente desde la opción
Perl->Debugger
del menú.. También se puede usar el ActivePerl
Development Kit, de ActiveState, pero
todavía no lo he usado tampoco . Por ejemplo, se pueden poner puntos de ruptura
con b
(breakpoint), para examinar qué es lo que va mal:
DB<1> b 3
Al ejecutar el programa con c
(continuar) o r
(ejecutar), el programa parará en esa línea:
DB<2> r main::(purga.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 que
main::(purga.pl:3): print "Total parcial ", $total+=$_[1], ",\n"; Bacterio DB<5> c Total parcial 1699441.75, main::(purga.pl:3): print "Total parcial ", $total+=$_[1], ",\n"; DB<5>
De esta forma, observamos 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 tercero desde el final, es decir, $_[$#_-2]
. 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
.
grep
... se puede hacer de una forma más simple. Nuestro político se dio cuenta
rápidamente que muchas de esas operaciones se pueden hacer en una sola línea, y
así ahorra uno espacio en disco duro, que va caro. Por ejemplo, el programa
congrep.pl
@zipi=<>;
grep( ( split(/ /) && print "Total parcial ",$total+=$_[$#_-2],"\n"),@zipi);
; en un par de líneas. En general, la función grep
, que recuerda
al grep
del sistema operativo, pero no tiene mucho que ver con
ella, sirve para procesar matrices enteras. Como primer argumente se le pasa una
expresión, pero ya se ve que en perl el concepto de expresión es un poco amplio;
de hecho, el programa anterior es uno de los ejemplos de programa críptico que
los perl-hackers tanto conocemos y amamos.
Pero en fin, vamos a ello: la expresión, para empezar, está entre paréntesis;
luego, consiste en dos partes separadas por 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
&&
; este
operador nos garantiza el orden de evaluación, y además no evalúa la parte de la
derecha si no ha podido ejecutarse la de la izquierda; en resumen, la expresión
hace lo mismo que las dos líneas del interior del bucle anterior... Y, ¿qué hace
grep
, entonces? Simplemente, le asigna a $_
cada uno
de los componentes de la matriz que se le pasa como segundo argumento, y crea
otra lista que tiene como componentes aquellos para los cuales la expresión es
verdadera. Por ejemplo, podemos crear a partir del anterior otro programa que
imprima sólo los que hayan pagado por encima de una pasta determinada, y además
los meta en una matriz (mailing.pl):@zipi=<>;
@ricos=grep( split(/ /) && ($_[$#_-2] > 200000) , @zipi );
print @ricos;
grep
.
Regularizando la situación
Expresiones regulares: comparación y sustitución
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 carácter, en la línea 4 del programa anterior), y cada vez
que hemos usado split
. Las expresiones regulares se usan para hacer
comparaciones, es decir, hallar si un texto sigue o contiene una determinada
expresión regular, y también para sustituir una subcadena que cumpla una
expresión regular por otra cadena.
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 //
,
o bien m{}
(las llaves pueden ser sustituidas por cualquier otro
elemento que no esté incluido en la expresión regular), 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 programilla
$_ = "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 punto como tal, 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 coincida con la expresión 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+)\/(\d+)
que puede
parecer un poco críptica (y en realidad lo es), pero cuyo significado se puede
resolver mirando la tabla 4. Con esta modificación, el bucle central del
programa totales.pl se
queda reducido a
while(<>) {
($paganini, $pasta, $hora, $dia, $mes) = /(\D+) (\d+\.?\d+) (\S+) (\d+)\/(\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 la tabla 4,
\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;
en
el bucle 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 toda la expresión regular anterior
como(\S+) (\S+) (\S+) (\S+)
funcionaría perfectamente.
Y para terminar,(\d+)\/(\d+)\/(\d+)
coincide con el día, el mes y
el año, 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 bucle
while(<>) { 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 inmediatamente a otra variable.
Toda la información sobre expresiones regulares está en la página de manual
perldoc perlre
.
|
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 listado siguiente.
|
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.sys
al 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.
[v5]En realidad, local
crea variables
globales pero con valores locales; es decir, se crea una variable que se puede
ver en todo el programa, pero cuyo valor se guarda al principio de la subrutina
y se recupera al final de la subrutina. En la versión 5 se introduce la palabra
clave my
, que declara verdaderas variables locales. En la versión
5, por tanto, se suele preferir my
para declarar las variables de
las subrutinas. [/v5]
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 la tabla 4), 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 4.
pack
y unpack
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 dejado 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.uu
y se crea el fichero codificado, que luego podrá uno contemplar con su visor de GIFs preferido (o escuchar con la SoundBlaster). Se pueden dar comodines en la línea de comandos; el bucle lo irá abriendo uno por uno.
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). Otra forma de usarlo es para formatear
ficheros de salida; usando
print pack("A7",$cadena)
, haremos que la salida ocupe justamente 7 caracteres,ni más mi menos; así aparecerá todo en bonitas columnas. Por ejemplo, nuestro archiconocido memoria.pl se podría convertir en este memoria-col.pl
|
Este programa, sobre el fichero siguiente
uno, 1605168 este es el dos, 166166 este puede ser el tres, 1616136
produciría la siguiente salida
uno 401292 19:54 25/7 este es el dos 41541.519:54 25/7 este puede ser e404034 19:54 25/7
(dependiendo, claro está, de cuando se hayan producido las susodichas transacciones cambiarán horas y fechas).
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; además, decide tirar Windows95 a la basura e instalalarse
Linux. 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/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 generalización. 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
tipolineas 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.
Como nuestro político corruptoTM ya es todo un guru de
perl, la secretaría de prensa del partido decide encargarle el
seguir en la Internet todos los sitios web que hablen del Señor X, para luego
poder así denunciar la conspiración judeo-masónica-telefónica-mundial (o séase,
de El Mundo) en contra de la
democracia y de sus figuras más prominentes. Por eso, decide hacer un programa
que cuente todas las páginas en las que aparece la cadena "Señor X"; así luego
puede hacer un gráfico con su evolución y ver si la cosa va a más o a menos.
Decide usar el buscador Altavista, ver el número de páginas que devuelve, e imprimirlo.
Para ello, hace el siguiente programa (altax.pl
):
|
Este programa imprimiría algo así como
Conectando a http://www.altavista.com/cgi-bin/query?pg=q&sc=on&hl=on&q=%22Se%F1or%20X%22&kl=XX&stype=stext&search.x=27&search.y=7
Encontradas 49 respuestas
Este programa, tal cual, seguro que no funciona en un ordenador. Usa un par
de librerías (o "bibliotecas" para los puristas), que no se encuentran en la
instalación estándar de PERL: la LWP::Simple
y la
URI::Escape
. Lo primero que habrá que hacer es bajarse e instalar
estas páginas.
Si está uno en Windows, es bastante fácil. Con la instalación de
PERL viene un programa, ppm
, que sirve
directamente para bajarse e instalar módulos. Simplemente tecleando ppm, y luego
ppm> install LWP::Simple
ppm> install URI::Escape
el programa baja, compila si es necesario, y prueba los dos módulos. A veces
esto que parece tan simple no lo es, porque necesita compiladores externos y
otra serie de cosas, pero si hay suerte, todo irá bien. Con ppm
se
pueden usar otras órdenes, tales como
ppm> search WWW
que dará una lista de todos los módulos que incluyan WWW en su nombre. En
este caso, además, las dos librerías vienen en un sólo paquete, el libwww-perl,
que se puede bajar uno sólo tecleando install libwww-perl
En Linux y otros Unixes, es un poco más complicado, pero no mucho. Todos los módulos para PERL se crean y empaquetan de una forma estándar. Tras localizarlos, habitualmente en alguno de los sitios CPAN (http://www.cpan.org), se hace lo siguiente:
[yo@localhost tmp]# tar xvfz libwww-perl-5.47.tar.gz
libwww-perl-5.47/
libwww-perl-5.47/t/
(más cosas...)
[yo@localhost tmp]# cd libwww-perl-5.47
[yo@localhost libwww-perl-5.47]# perl Makefile.PL
(aquí comprobará que todas las librerías que se necesitan previamente
están instaladas, y crea un Makefile)
[yo@localhost libwww-perl-5.47]# make
[yo@localhost libwww-perl-5.47]# make install
(y también, si se quiere)
[yo@localhost libwww-perl-5.47]# make test
En este caso, también se acaba antes bajándose, para empezar, el módulo
CPAN.pm
, con el cual, tecleando perl -MCPAN -e shell;
tiene uno un entorno similar al que hemos mencionado antes para Windows. Eso sí,
para instalar algo hará falta, tanto en este caso como en el anterior, ser
superusuario.
Las librerías no sólo instalan el código, instalan también los manuales
correspondiente. En este caso, se puede consultar el manual escribiendo
perldoc LWP
; y merece la pena hacerlo, porque nos encontraremos
muchas cosas interesantes. Como indicamos en la sección sobre el zen del
PERL, merece la pena conocer bien las librerías antes de
ponerse a hacer cualquier cosa en PERL, porque en la mayoría de
los casos se encontrará con la mitad del trabajo hecho.
Además, en este caso las librerías son dos palabras separadas por
::
; esto es porque los módulos están organizados en una especie de
jerarquía. Cada módulo puede estar dentro de una jerarquía o no, dependiendo de
lo que quiera el autor y dependiendo de su funcionalidad. En este caso,
Simple
está dentro de LWP (lib-www-perl), porque va de eso; pero
hay muchas más de la misma familia (por ejemplo, LWP::UserAgent
un
módulo más flexible para bajarse páginas). Lo mismo ocurre con el otro
módulo.
Ya que tenemos las librerías instaladas, podemos ir al programa. Las dos
líneas que empiezan por use
importan las librerías. En realidad,
hacen algo más que eso: traen todos los identificadores de la librería al
espacio global de nombres, de forma que podemos usar las subrutinas y variables
de la librería como si hubieran sido declaradas en el mismo programa. En
realidad, en este caso usamos sólo una función de cada librería, así que, para
no contaminar el espacio de nombres, podríamos haber escrito
use LWP::Simple qw(get);
use URI::Escape qw(uri_escape);
con exactamente el mismo resultado. Esta expresión, aunque pueda parecer un
poco rara, usa el mecanismo quote del PERL, que sirve
para definir matrices, cadenas y otras cosas sin necesidad de escribir
demasiado; o séase, que se ahorra uno comillas, comas y cosas por el estilo.
qw( a b c)
equivaldría a ('a','b','c')
. Por supuesto,
el operador qw
y todos los demás por el estilo (ver la página de
manual perlop) pueden usarse en cualquier punto del programa
PERL.
Hasta ahora, en realidad, no hemos hecho nada, salvo importar las librerías;
el programa de verdad empieza en la línea 6, que requiere un poco de
explicación. Usa la subrutina uri_escape
del módulo
URI::Escape
, que sirve para codificar cadenas que incluyan
caracteres no alfanuméricos de forma que se pueda incluir dentro de un URL o
dirección Web, tal como la que usan los buscadores. En este caso, la cadena
contiene espacios, comillas y la letra ñ, que no puede ir tal cual en un URL.
Por eso se codifica, quedando así: %22Se%F1or%20X%22
. Generalmente,
cuando se vayan a hacer peticiones a buscadores desde un programa, habrá que
hacer este tipo de codificación.
El URL que se va a solicitar se compone en la siguiente línea. El único truco
aquí es ver que la petición que se hace a Altavista va dentro de la variable
q
(no tienes más que hacer una búsqueda de cualquier palabra, y ver
dónde aparece en el URL de la respuesta); eso se puede ver mirando al código
HTML de la página, si no es demasiado complicado. En este caso, encontramos una
línea HTML que dice:
<input type=text name=q size=35 maxlength=800 value="">
con lo cual, se ve que la variable q
es la que contiene la
petición del usuario. Algo similar se puede hacer en casi todos los demás
buscadores.
La dos líneas siguientes imprimen un mensaje, y se bajan efectivamente el
URL. La función get
está sacada del módulo
LWP::Simple
, y simplemente se baja una página y la mete en una
variable. También se puede imprimir usando getprint
o meterse en un
fichero usando getstore
. Como siempre, más información escribiendo
perldoc LWP::Simple
.
Finalmente, usando expresiones regulares (¿recuerdas las expresiones regulares? Mira en Regularizando la situación) se extrae el número de páginas que se han encontrado, y se imprime. Y nuestro político corrupto, finalment, comprueba que últimamente ya casi ni se habla del tema.
|
Ya que hemos aprendido todo lo que debíamos aprender sobre PERL, no está de más dar unos cuantos consejos para realizar buenos programas.
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.
Conoce tus bibliotecas
Aparte de las muchas librerías estándar que trae PERL, hay otras, disponibles sobre todo en el CPAN, para hacer casi todo, desde acceso a base de datos hasta realizar tablas astrológicas. El conocer qué librerías hay disponibles, o mirar antes ahí, ahorrará mucho trabajo.
print "Content-type: text/html\n\n";
print "Hola";
. Por supuesto, habrá que colocarlo en la zona correspondiente
del servidor para que sea accesible como CGI. Otra forma, a partir de la
versión 5, es:
use CGI;
print header, start_html, title('titulo'),end_html;
que usa el módulo estándar CGI.pm, que sirve precisamente para
escribir CGIs. Más información, escribiendo perldoc CGI
Win32::ODBC
. Con cualquiera de estos dos
módulos, se puede usar con cualquier SGBD; en Linux/Unix, se puede usar con
MySQL o MiniSQL, o Postgres; y en Windows se puede usar con Microsoft SQL
server combinado con ODBC. Eso sí, como es inherente a las conexiones ODBC, es
más bien tirando a lento. fork
no funciona (salvo en la
nueva versión 5.6), el acceso a base de datos no se hace igual (se usa el
módulo DBD/DBI en Linux y en Windows, generalmente, el Win32::ODBC, aunque
también se puede usar el mismo); para ejecutar un programa en PERL hay que
definir una serie de cosas. Pero en general, cualquier programa que no use
ninguna característica específica de un sistema operativo funciona exactamente
igual en los dos sistemas operativos. Y si hay alguna duda, se puede insertar
código particular detectando el valor de la variable $OSNAME, $^O
o $Config{osname}
. Por ejemplo, se puede hacer algo así:
if ( $^O eq 'linux' ) {
[código específico para Linux];
} else {
[código para otros SOs menos afortunados];
}
JJ Merelo Depto. Arquitectura y Tecnología de Computadores Facultad de Ciencias Campus Fuentenueva, S/N 18071 Granada (España)Todo esto, por supuesto, afecta también a cualquier parte del tutorial o a los ejemplos que se incluyen con el mismo.
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:
En castellano, se pueden encontrar los siguientes libros en BOL : | |
![]() PERL. Páginas Web interactivas Palacio, Juan El único libro escrito en España. Muy completo, y enfocado principalmente a la programación de CGIs. |
![]() Guía de referencia para programadores de PERL 5 Duncan, Luke, Wyke, R. Allen |
![]() Husain, Kamran |
![]() Programación en CGI con Perl 5 Herrmann, Eric |
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).
http://www.perl.com/
, sitio central
dedicado al PERL. Este sitio contiene noticias, listas de
correo, enlaces a todos los sitios de donde puede uno bajarse las diferentes
versiones de PERL.
http://www.pm.org
) están dedicados a grupos de usuarios de
perl; y también hay un http://www.perl.org/.
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
.
ftp://src.doc.ic.ac.uk/packages/ibmpc/simtel/perl
;
en el mismo sitio hay versiones para el Mac y el Amiga. Además, hay una serie de sitios sobre PERL que están en castellano
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. Aunque en realidad Windows NT no lo hace demasiado bien.