Hogar Mapa Indice Busqueda Noticias Arca Enlaces Sobre LF
[Top bar]
[Bottom bar]
[Photo of the Author]
por Guido Socher

Sobre el Author:
Guido es un veterano fan de Linux y hacker de Perl. Su página sobre Linux se encuentra en www.oche.de/~bearix/g/

Contenidos:

  1. Arrays
  2. Bucles para recorrer arrays
  3. La línea de comando
  4. Los arrays como pilas
  5. Lectura de directorios
  6. Un buscador de ficheros
  7. Referencias

Perl (Parte III)

[Illustration]

Resumen:

Perl parte I ofreció una primera visión general de Perl. En Perl parte II se escribió el primer programa útil. La parte III se centrará en el estudio de los arrays.



 

Arrays

Un array es una lista de variables a las que se puede acceder mediante un índice. Ya vimos que los nombres de las variables "normales" -también llamadas variables "escalares"- comienzan con un signo dólar ($). Pues bien, los nombres de los arrays comienzan con un signo arroba (@). Como un array contiene varias variables escalares, se debe usar un signo dólar para referirse a cada uno de sus elementos individuales.

Veamos un ejemplo:
 

!/usr/bin/perl -w 
# vim: set sw=8 ts=8 si et: 
# declaración de una nueva variable de array: 
my @miarray; 
# inicialización de la variable con algunos datos: 
@miarray=("dato1","dato2","dato3"); 
# acceso al primer elemento (el de índice 0): 
print "el primer elemento de miarray es: $miarray[0]\n"; 

Obsérvese que escribimos @miarray para referirnos a todo el conjunto, y $miarray[0] para referirnos a elementos individuales.

En Perl, los arrays comienzan en el índice 0. Según se añaden datos, se van creando automáticamente nuevos índices, de modo que no es necesario conocer el tamaño del array en el momento de la declaración.

Se puede inicializar un array con una serie de datos sin más que listar esos datos separados por comas y entre paréntesis. Por ejemplo, ("dato1","dato2","dato3") es en realidad un array anónimo (es decir, sin nombre). Por tanto, podemos poner ("dato1","dato2","dato3")[1] para referirnos a su segundo elemento:
 

!/usr/bin/perl -w 
print "El segundo elemento es:" 
print ("dato1","dato2","dato3")[1]; 
print "\n"
 

Bucles para recorrer arrays

El bucle foreach permite recorrer todos los elementos de un array. Este bucle trabaja de la siguiente forma:
 
#!/usr/bin/perl -w 
# vim: set sw=8 ts=8 si et: 
my @miarray =("dato1","dato2","dato3"); 
my $varb; 
my $i=0; 
foreach $varb (@miarray){ 
print "el elemento número $i es $varb\n"; 
$i++; 

La ejecución del programa anterior produce la siguiente salida:
 

el elemento número 0 es dato1 
el elemento número 1 es dato2 
el elemento número 2 es dato3

La instrucción foreach toma cada elemento del array y lo asigna a una variable de bucle ($varb en el ejemplo anterior).

Es importante destacar que los valores de los elementos del array no se copian directamente a la variable de bucle. Por el contrario, la variable de bucle es una especie de puntero, de modo que cualquier modificación que sufra se refleja en los elementos del array.

El siguiente programa pasa a mayúsculas todos los elementos del array. La instrucción de perl tr/a-z/A-Z/, similar a su homónima de Unix, pasa todas las letras a mayúsculas.
 

#!/usr/bin/perl -w 
# vim: set sw=8 ts=8 si et: 
my @miarray =("dato1","dato2","dato3"); 
my $varb; 
print "antes:\n"; 
foreach $varb (@miarray){ 
print "$varb\n"; 
$varb=~tr/a-z/A-Z/; 

print "\ndespués:\n"; 
foreach $varb (@miarray){ 
print "$varb\n"; 
}

Ejecutemos el programa anterior. En el segundo bucle se aprecia todos los elementos de @miarray han sido pasados a mayúsculas:
 

antes:
dato1
dato2
dato3

después:
DATO1
DATO2
DATO3
 

La línea de comando

En Perl II vimos que la función de biblioteca &getopt permite obtener la línea de comando y cualquiera de las opciones contenidas en ella. &getopt es similar a su homónima de C.

En Perl, el contenido de la línea de comando se asigna a un array llamado @ARGV. La función &getopt se limita a leer los valores de los elementos de @ARGV.

A diferencia de C, el contenido del primer elemento del array no es el nombre del programa, sino el primer argumento de la línea de comando (si se desea conocer el nombre del programa, entonces debe recurrirse a $0, pero esto sería objeto de otro artículo...).

He aquí la salida de un programa de ejemplo llamado suma. Lee dos números en la línea de comando y los suma...:
 

> suma 42 2 
42 + 2 es:44

... y éste es el código del programa:
 

#!/usr/bin/perl -w 
# se comprueba si hay dos argumentos: 
die "USO: add numero1 numero2\n" unless ($ARGV[1]); 
print "$ARGV[0] + $ARGV[1] es:", $ARGV[0] + $ARGV[1] ,"\n";
 

Los arrays como pilas

Perl dispone de una serie de funciones que permiten tratar a un array como una pila. El siguiente programa añade dos elementos a un array existente:
 
#!/usr/bin/perl -w 
my @miarray =("dato1","dato2","dato3"); 
my $varb; 
print "el array:\n"; 
foreach $varb (@miarray){ 
print "$varb\n"; 

push(@miarray,"a"); 
push(@miarray,"b"); 
print "\ndespués de añadir \"a\" y \"b\":\n"; 
while (@miarray){ 
print pop(@miarray),"\n"; 
}

pop elimina elementos del final del array, y el bucle while se sigue ejecutando hasta que el array esté vacío.  

Lectura de directorios

Perl dispone de las funciones opendir, readdir y closedir, que permiten leer el contenido de un directorio. En particular, readdir devuelve un array con los nombres de todos los ficheros. Mediante un bucle foreach, se pueden recorrer los nombres de fichero y buscar uno determinado.

He aquí un programa sencillo que busca en el directorio actual el nombre de un fichero determinado:
 

#!/usr/bin/perl -w 
# vim: set sw=8 ts=8 si et: 
die "Uso: busca_en_dir_actual fichero\n" unless($ARGV[0]); 
opendir(DIRHANDLE,".")||die "ERROR: no se puede leer directorio actual\n"; 
foreach (readdir(DIRHANDLE)){ 
print"\n"; 
print "encontrado $_\n" if (/$ARGV[0]/io); 

closedir DIRHANDLE;

Echemos un vistazo al programa. En primer lugar, se comprueba si el usuario ha proporcionado un argumento en la línea de comando. Si no es así, se muestra información de ayuda y se sale del programa.

Seguidamente, se abre el directorio actual ("."). opendir es similar las funciones open para ficheros. El primer argumento es un handle (identificador), que es necesario pasar a las funciones readdir y closedir. El segundo argumento es la ruta al directorio.

Ahora viene el bucle foreach. Nótese que en este ejemplo no hay variable de bucle. Perl crea automáticamente una variable de bucle llamada $_. readdir(DIRHANDLE) devuelve un array, y entonces se usa foreach para examinar cada uno de sus elementos.

Mediante /$ARGV[0]/io se comparan las expresiones regulares contenidas en $ARGV[0] con la variable $_. El io significa que la búsqueda ignora mayúsculas y minúsculas, y que la expresión regular se compila sólo una vez. Esto último hace que el programa se ejecute más rápidamente. Conviene proceder así cuando se tenga una variable dentro de una expresión regular y se esté seguro de que el valor de esta variable no cambiará durante la ejecución del programa.

Probemos el programa. Supongamos que tenemos en el directorio actual los ficheros article.html, array1.txt and array2.txt. Pues bien, una búsqueda de "HTML" se haría de la siguiente forma:

>busca_en__dir_actual HTML
.
..
article.html
found article.html
array1.txt
array2.txt
Nótese que la función readdir ha encontrado dos ficheros más: "." y "..". Se trata, como sabemos, del directorio actual y de su directorio padre.  

Un buscador de ficheros

Querría terminar este artículo con un programa algo más complejo (¡pero también más útil!). Se trata de un buscador de ficheros. Llamémoslo pff (perl file finder). Básicamente, se comporta como el programa anterior, pero busca también en subdirectorios.

¿Cómo podemos construir este programa? Ya tenemos código para leer el directorio actual y buscar ficheros en él. Habrá que comenzar en el directorio actual y, cuando nos encontremos un fichero que a su vez es también un directorio (excepto . y ..), buscar también en él.

Este sería el pseudocódigo de este típico algoritmo recursivo:

sub busca_fich_en_dir(){
  my $dir=shift;
  ...leer el directorio $dir ....
  ...si un fichero es a su vez un directorio 
    entonces ejecuta &busca_fich_en_dir(ese fichero)....
}
Obsérvese que para comprobar si un fichero es un directorio y no un enlace simbólico a un directorio, se usa:

if (-d "$file" && ! -l "$dir/$_"){....}.

Ya tenemos toda la funcionalidad que necesitamos, de modo que podemos escribir el código fuente (pff.gz).
 

#!/usr/bin/perl -w 
# vim: set sw=8 ts=8 si et: 
# autor: guido socher, copyright: GPL 

&help unless($ARGV[0]); 
&help if ($ARGV[0] eq "-h"); 

# comenzamos en el directorio actual: 
busca_fich_en_dir("."); 
#----------------------- 
sub help{ 
print "pff -- perl regexp file finder 
USO: pff [-h] regexp 

pff busca en el directorio actual y todos sus subdirectorios
ficheros que coincidan con una expresión regular. 
La búsqueda no tiene en cuenta mayúsculas ni minúsculas. 

EJEMPLO: 
buscar un fichero que contiene la cadena foo: 
pff foo 
buscar un fichero que termina en .html: 
pff \'\\.html\' 
buscar un fichero que comienza con la letra \"a\": 
pff \'^a\' 
buscar un fichero que se llame article.html: 
pff \'article.*html\' 
Nótese que se usa .* y no * 
\n"; 
exit(0); 

#----------------------- 
sub busca_fich_en_dir(){ 
my $dir=shift; 
my @flist; 
if (opendir(DIRH,"$dir")){ 
@flist=readdir(DIRH); 
closedir DIRH; 
foreach (@flist){ 
# ignorar . y .. : 
next if ($_ eq "." || $_ eq ".."); 
if (/$ARGV[0]/io){ 
print "$dir/$_\n"; 

busca_fich_en_dir("$dir/$_") if (-d "$dir/$_" && ! -l "$dir/$_"); 

}else{ 
print "ERROR: no se puede leer el directorio $dir\n"; 


#-----------------------

Examinemos el programa. En primer lugar, se comprueba si el usuario ha proporcionado un argumento en la línea de comando. Si no es así, se trata de un error, por lo que imprimimos un texto de ayuda para el usuario. También se imprime un texto de ayuda si se ha indicado la opción -h.

La búsqueda se inicia en el directorio actual. Se usa el algoritmo recursivo descrito anteriormente: leer el directorio, buscar los ficheros, comprobar si un fichero es un directorio, si lo es ejecutar de nuevo busca_fich_en_dir().

En la instrucción que comprueba los directorios, se comprueba también que no se trata de un enlace (link) a otro directorio. Es necesario hacer esto porque alguien podría haber creado un enlace simbólico a "..", lo cual haría que el programa se ejecutara indefinidamente.

"eq" es el operador de comparación de cadenas de perl. Mediante

next if ($_ eq "." || $_ eq "..");

se comprueba si el contenido de la variable $_ es igual a ".." o ".". Si es igual, se ejecuta la instrucción "next". Dentro de un bucle foreach, una instrucción "next" significa "volver al comienzo del bucle, pero usando el siguiente elemento del array". Es similar a la instrucción "continue" del lenguaje C.  

Referencias

He aquí una relación de otros tutoriales interesantes de Perl.
Contactar con el equipo de LinuFocus
© Guido Socher
LinuxFocus 1999
Translation information:
de -> -- Guido Socher
en -> es José Ramón García de Madariaga

1999-11-10, generated by lfparser version 0.6