"La Gaceta de Linux...¡haciendo de Linux algo un poco más divertido!"


Tutorial de Perl, primera parte

Por: Ben Okopnik

Traducción al español por David Chiner
el día 09 de Abril, 2002, para La Gaceta de Linux


Por poco tiempo que lleves utilizando Linux, seguramente habrás oído hablar de Perl; probablemente hayas ejecutado unos cuantos scripts,quizás incluso sin saberlo. Programas como "inews", "mirror", "debconf", "majordomo", "sirc" y unos cuantos más están escritos en Perl. Un rápido "zgrep" sobre el archivo "Packages.gz" de la distribución Debian me dice que 382 de sus paquetes dependen de Perl (es decir, que una parte fundamenteal de dichos paquetes está escrita en ese lenguaje), y que otros 20 paquetes lo sugieren o lo recomiendan.
 

¿Para qué sirve?

"Perl es estupendo para procesar texto, y es estupendo para conjuntar e integrar cosas. En un lenguaje de scripts, todos estos aspectos vienen a ser lo mismo."
 -- John Ousterhout, creador del Tcl scripting language

Se supone que "Perl" son las iniciales de "Practical Extraction and Report Language" (Lenguaje Práctico de Extracción y Reporte). De acuerdo: la-ta-zo, pero creo que necesitarás saberlo si quieres convencer a $ABURRIDOS_REUNIDOS_SA para que lo use. En realidad, el creador del Perl, Larry Wall <larry@wall.org> dice en la man page de Perl: "Perl son de hecho las iniciales de Pathologically Eclectic Rubbish Lister (Listador Basura Patológicamente Ecléctico). Como quieras Larry. No se lo diré a nadie.

Perl ha sido diversamente descrito como "Un lenguaje de scripts con inconvenientes de lenguaje coloquial", "La motosierra suiza de Unix", "La cinta aislante de la red", y otros nombres digamos... poco amigables. Ha sido utilizado para escribir scripts de una sola línea, programas de rápida ejecución, grandes proyectos (todo el sistema de producción y control editorial de Amazon.com, el sistema de gestión y envío de contenidos de Netscape, el Proyecto de Genoma Humano para secuenciar el ADN, etc.), y millones de programas rápidos que realizan una increíble variedad de cosas.  Perl también puede emular unas cuantas utilidades comunes del sistema Unix. (nota: si estás pensando en aprender 'awk', 'sed', 'grep', y 'tr' te aconsejo que empieces con Perl. Tendrás la misma funcionalidad, más rápida, y nunca te faltarán recursos. Si yo lo hubiese sabido en su momento... )

Como se espera de cualquier lenguaje moderno, Perl admite la programación orientada a objetos. También incluye herramientas de red (sockets, etc.), es muy portable (un script bien escrito funcionará sobre Linux, BSD, Solaris, DOS, Win9x, NT, MacOS, OS/2, AmigaOS, VMS, etc. sin modificación alguna), y tiene un ciclo muy corto de escritura y verificación -como no requiere compilación, sólo tienes que escribir los cambios y ejecutar el script. Existe un gran número de módulos (rutinas Perl predefinidas) disponible para realizar cualquier tarea; la Comprehensive Perl Archive Network (CPAN) es uno de los mejores recursos que puede tener un programador de Perl.
 

Pero, ¿qué es Realmente?

Buena pregunta, espero que tras usarlo durante unos años, me lo puedas explicar a mí. Una descripción de cualquier cosa es una caja... y sigo buscando una lo bastante grande para que quepa Perl (y que a ser posible tenga una buena cerradura).
 

¿Para qué tipo de cosas no sirve el Perl?

Pues bien, no me veo escribiendo un procesador de texto GUI, un videojuego o un explorador gráfico con este lenguaje. De hecho, con Perl se pueden construir deslumbrantes front ends gracias a sus interfaces con muchos otros lenguajes, y por tanto podrías hacer todas esas cosas -pero en mi opinión, existen formas más eficientes de hacerlas en otros lenguajes. Perl no es la piedra filosofal- ¡que el programador vaya con cuidado!

Observa también que Perl no está escrito a su vez en Perl; tampoco el kernel de Linux. Estas cuestiones de bajo nivel mejor dejárselas al C o al C++ con algo de ensamblador metido; el lema de todo programador debería ser 'a cada tarea le corresponde su herramienta'.
 

Un último aviso antes de meternos en faena

Si ya sabes un poco de Perl y ves en estas entregas algo del tipo 'Pues a mí no me lo explicaron así', recuerda simplemente el lema de Perl: There's More Than One Way To Do It (Existe Más De Una Manera De Hacerlo). Esta filosofía fundamental de Perl suele abreviarse como TMTOWDI y pronuciarse "tim-today". Obviamente, las correcciones de cualquier error obvio son bienvenidas.

Quienes hayan leído mis anteriores entregas sobre escritura de shells quizás recuerden que un script empieza con la llmada línea "bash-gang" o "shebang":

#! /bin/bash

De esta manera, el shell recibe la orden de crear un subshell para que el programa especificado ejecute el código que viene a continuación. Esto también se cumple en los scripts de Perl. La primera línea debe ser

#! /usr/bin/perl

o cualquiera que sea el path correcto de tu ejecutable "perl".

Recuerda los requisitos de una línea hash-bang:

1) Debe ser la primera línea del script.
2) El hash (#) debe ser el primer carácter de la línea, y no puede
   haber nada entre éste y el bang (!).
3) Debes usar el path absoluto, no sólo el nombre del ejecutable.

Y ahora, escribamos nuestro primer script en Perl:


#!/usr/bin/perl
# "Adiós" - un moderno y angustiado sustituto del "Hola Mundo"

print "¡Adiós mundo cruel!\n";
unlink $0;

Bueno, al menos se despide antes de marcharse; doña Buenos Modales estaría orgullosa.¿Qué acabamos de hacer? Muchas cosas bastante obvias: primero, el "hash_bang"; después, una línea indicando lo que hace el script -otra aspecto heredado de la escritura de shells, y que es una execelente idea (¡nos ahorramos llenar el código de comentarios!). A continuación, imprimimos el mensaje mediante la función 'print'. Observa el "\n" al final de la cadena de texto: Perl no aporta automáticamente un salto de línea, debes decidir si lo quieres o no. Observa también, el punto y coma al final de la sentencia: igual que en C, Perl lo exige, ¡pobre del programador que lo olvide! En realidad, la detección de errores en Perl es bastante cómoda, con mensaje relativamente legibles; pero como el punto y coma es un separador de sentencias, a menudo se reporta el error como si estuviese en la línea siguiente. Si eres consciente de este imprevisto, no tienes por qué tener problemas. Mejor aún, recuerda simplemente que debes usar puntos y comas.

La última línea realiza la acción diabólica de borrar el archivo ("adiós mundo cruel" llevado a la práctica). El "$0" es simplemente una referencia al nombre del script que se está ejecutando, y "unlink" hace lo mismo que "rm". Observa que "$0" es mucho más útil que "goodbye" o incluso "./goodbye" ("$0" devuelve el nombre del archivo, cualquiera que sea).
 

Por cierto, algunos consejos de programación

No pretendo ni mucho menos ser perfecto escribiendo código: en el pasado he escrito códigos que harían sonrojar a cualquiera. La cuestión es que continuamente intento mejorar -y me gustaría que esta idea diese sus frutos.

Perl trata los "espacios en blanco" -tabuladores y espacios- de la forma que se merecen, esto es, ignorándolos. Eso te permite estructurar tu código Perl para para dar una idea de lo que estás haciendo. Veamos un sencillo ejemplo:

@yates = ("Aloa", "Cheoy Lee", "Pearson", "Mason", "Swan", "Wetsnail", "S2", "Petersen", "Hereshoff");  # Lista de veleros

Acabamos de llenar un array llamado '@yates' con nombres de veleros.De acuerdo, así funciona, pero podría ser más comprensible:

@yates = ("Aloa",       # Velero OSTAR/IOR francés
         "Cheoy Lee",   # Cómodo pero caro
         "Pearson",     # Sólido pero bastante pesado
         "Mason",       # Bien diseñado pero un poco traicionero
         "Swan",        # Barco clásico (si puedes pagarlo)
         "Wetsnail",    # Los Wetsnails están muy bien para principiantes
         "S2",          # Bonito barco de recreo, no apto para travesías
         "Petersen",    # Crucero de acero, espacioso pero lento
         "Hereshoff");  # Rápido y bonito; incómodo y caro

Estos hábitos de programación no sólo se aplican a Perl. Los lenguajes modernos permiten añadir espacios en blanco para que el código sea más legible. A través de estas entregas intento mostrar al menos mi propia versión de un buen estilo de codificación; me gustaría que lo tuvieses en cuenta cuando escribas tu código.
 

Variables

Perl se centra en la facilidad de uso. Es un lenguaje de "escritura aproximada" cuya definición de variables no está aprisionada en camisas de fuerza; por ejemplo, no hay manera de definir una variable para que sólo contenga un número positivo de 32 bits con coma flotante.

Los tres tipos de variables en Perl son los escalares, los arrays, y los hashes. A pesar de sus terroríficos nombres, son más bien simples: meras variables que contienen diferentes disposiciones de datos.

escalares - números, strings, o referencias
Una variable escalar se denota por un signo '$', por ejemplo $num, $joe, $puntero
Ejemplos:
"0.0421", "rincón de Joe", posición de memoria "0xA000"

arrays - lista ordenada de escalares
Una variable de tipo array se denota con un signo'@', es decir @v, @lista, @variable
       Ejemplo:
   0 - "Domingo"
   1 - "Lunes"
   2 - "Martes"
   3 - "Miércoles"
   ...

hashes - lista de escalares referenciados por clave
Los hashes se denotan con un '%', es decir %gente, %x, %esto_es_un_hash
       Ejemplo:
   a la atención de - "Sherlock Holmes"
   dirección        - "221B Baker Street"
   código postal    - "NW1"
   ciudad           - "Londres"
   país             - "Gran Bretaña"
   profesión        - "detective"
   ...

Observa que, a diferencia de los arrays, los hashes no se guardan en orden numérico (devolver el primer elemento de un hash casi nunca tiene que ver con devolver el primer elemento que se introdujo). Los elementos de un hash son referidos por sus claves, no por sus posiciones en la estructura.

Estos tres tipos de datos pueden contener (o apuntar a) lo que quieras (y acceder fácilmente).

Otra nota importante: $a, @a, y %a no mantienen ninguna relación entre sí. Están en espacios de nombres diferentes. Procuro no usar nombres visualmente conflictivos como esos en mis programas, especialmente porque existen cosas como $a[0] (una referencia al primer elemento del array @a). Debes tener cuidado con esas cosas.
 

Como estas variables pueden contener tipos diferentes de datos -numéricos y strings- necesitarás operadores que funcionen con ambos tipos. Perl los pone a tu disposición, y debes recordar a qué tipo corresponde cada cual:

   Operador                   Num     Str
   --------------------------------------
   Igual                      ==     eq
   Diferente                  !=     ne
   Menor que                  <     lt
   Mayor que                  >     gt
   Menor o igual que          <=     le
   Mayor o igual que          >=      ge

Un truco para no olvidarte: cuando compares letras (strings) usa letras.

Si quieres un ejemplo práctico, este te pondrá al borde del ataque de nervios:

#!/usr/bin/perl
# Un script de evaluación política

$a = "Al";
$b = "George";

if ( $a > $b)   { print "$a sería mejor presidente.\n"; }
if ( $a < $b)   { print "$b sería mejor presidente.\n"; }
if ( $a == $b)  { print "$a y $b son lo mismo...\n"; }

Hmm. El resultado dice que son lo mismo. Quizás refleje la realidad política pero, ¿qué pasa con nuestras comparaciones?... De acuerdo. Deberíamos usar operadores de string, ¿no?


#!/usr/bin/perl
# Un script de evaluación política

$a = "Al";
$b = "George";

if ( $a gt $b)   { print "$a sería mejor presidente.\n"; }
if ( $a lt $b)   { print "$b sería mejor presidente.\n"; }
if ( $a eq $b)   { print "$a y $b son lo mismo...\n"; }

Ahora los operadores de comparación funcionan adecuadamente (y el sentido común se cumple... pero estoy divagando.)

Por cierto, ¿por qué en el primer ejemplo Perl decidió que "Al" era lo mismo que "George"? ¿Desde cuando los programas tienen ideas políticas?

La razón es de peso, tiene que ver con la manera como Perl distingue "true" de "false". Conviene saberlo porque todas nuestras verificaciones ("if", "while", "until", etc.) dependen de esta distinción.

"0" es "false", tanto si es un número como un string.
Todas las variables no definidas (las que no tienen un valor asignado) son "false".
Una cadena vacía - "" o '' - es "false".
Cualquier otra cosa es "true".

Practiquemos un poco. Echa un vistazo a estos valores y di si son "true" o "false":

"00"    "-1"    " "    "5 - 5"

Encontrarás las respuestas en la nota [1] al final de este artículo.
 

Otro aspecto importante es la interpolación de variables, que es una manera de determinar si algo entre comillas es interpretado o no. Veamos un ejemplo:

$nombre =  'Bessie';
print    'Nuestra vaca se llama $nombre.';

Oops. En la consola leemos

Nuestra vaca se llama $nombre.

No creo que ninguna vaca con un mínimo de autoestima acepte que la llamen así (por no mencionar lo difícil que es pronunciarlo). ¿Qué hacemos pues para que venga Bessie?

# Observa las comillas dobles donde antes estaban las simples!
$nombre =  'Bessie';
print    "Nuestra vaca se llama $nombre.";

Tenga éxito con los animales via Perl (y que nadie se imagine lo que no es). Te dije que podrías hacer cualquier cosa.

¿Y si queremos imprimir el nombre de la variable? Perl también te lo pone fácil.

$joe =   "Joe";
print    "La variable \$joe contiene el valor $joe.";

Podemos imprimir cualquier metacaracter (un carácter con un significado especial en Perl) si lo escapamos (lo escribimos precedido de una barra invertida). Echa un vistazo a esto:

$joe =   "Joe";
print    "La variable \"\$joe\" contiene el valor \"$joe.\"";

Uh... TMTOWDI:

print    'La variable "$joe" contiene el valor "', $joe, '".';

Seguro que entiendes la diferencia. Observa que los valores del método "print" estan separados por comas (sin comas tendrían un valor completamente diferente que discutiremos en un próximo artículo).

Antes de concluir, una consideración importante: cuando escribas tus scripts usa siempre el parámetro "-w" como parte del hash-bang

#! /usr/bin/perl -w

De este modo aparecerán warnings indicando los problemas surgidos en tu scritp. Asegúrate de que lo usas si eres principiante en Perl... y asegúrate el doble si eres un experto en Perl. Los errores no desaparecen a medida que progresas, se vuelven más listos. :)
 

Conclusión

En esta etapa de nuestro viaje hemos recalado en las aguas de una simple introducción. El mes que viene profundizaremos un poco más en el tema; quizás exploremos los arrays y los hashes, y quizás nos sumerjamos de cabeza en las increíblemente profundas "expresiones regulares", o "regexes" de Perl. Te sugiero que mientras tanto pruebes algo de lo que hemos hablado, que quizás experimentes un poco por tu cuenta (he descubierto que la mejor manera de aprender un lenguaje practicar hasta que no sabes más, y después exponer tus frustraciones a alguien que sepa más que tú. No recibirás las respuestas correctas si ni siquiera conoces las preguntas.

¡Que disfrutes con Perl!
 

Ben Okopnik
perl -we '$@="\145\143\150\157\040\042\112\165\163\164\040\141\156".
"\157\164\150\145\162\040\120\145\162\154\040\110\141\143\153\145\162".
"\042\040\076\040\057\144\145\166\057\164\164\171";`$@`'

Nota [1]: Todos "true". Ninguno de ellos se ajusta a la categoría "false": "00" no es lo mismo que "0"; tampoco "-1". Un espacio, " ", no es lo mismo que un string vacío (""), y "5 - 5", a menos que se evalúe, no es "0".

Bibliografía: "Perl: The Complete Reference", Martin C. Brown

man pages de interés (disponibles en cualquier sistema adecuadamente configurado para Perl):

perl      - visión general               perlfaq   - FAQs
perltoc   - doc TOC                perldata  - estructuras de datos
perlsyn   - sintaxix                 perlop    - operadores y precedencia
perlrun   - ejecución              perlfunc  - functions predefinidas
perltrap  - trampas para incautos   perlstyle - manual de estilo

"perldoc" and "perldoc -f"


Copyright © 2000, Ben Okopnik.
Copying license http://www.linuxgazette.com/copying.html
Publicado en el número 61 de Linux Gazette, Enero 2001