El operador chdir nos permite cambiar de directorio dentro de la jerarquía de directorios. Estudie el siguiente ejemplo:
lhp@nereida:~/Lperl/src/dir$ cat -n chdir.pl 1 #!/usr/bin/perl -w 2 use strict; 3 4 my $directory = shift; 5 my $expreg = (shift or '.*'); 6 $expreg = qr/$expreg/o; 7 chdir $directory or die "no encuentro $directory: $!"; 8 my @files = `ls -a`; 9 10 for (@files) { 11 next unless /$expreg/; 12 chop; 13 printf "$_\n"; 14 }Sigue un ejemplo de ejecución:
lhp@nereida:~/Lperl/src/dir$ chdir.pl /tmp/ '\d+$' IO-Tty-1.02 Parallel-Simple-Pipe-0.01 ssh-eSbkNq1402 v274545
Use el módulo Cwd:
use Cwd; my $dir = getcwd; use Cwd 'abs_path'; my $abs_path = abs_path($file);
Utilice la función abs_path en Cwd para conocer el camino absoluto de un fichero o directorio:
pp2@nereida:~/src/perl/testing$ perl -wde 0 DB<1> use Cwd 'abs_path' DB<2> x abs_path('add.pl') 0 '/home/pp2/src/perl/testing/add.pl' DB<3> x abs_path('./add.pl') 0 '/home/pp2/src/perl/testing/add.pl' DB<4> x abs_path('/home/pp2/src/perl/testing/add.pl') 0 '/home/pp2/src/perl/testing/add.pl' DB<5> x abs_path('../testing/add.pl') 0 '/home/pp2/src/perl/testing/add.pl' DB<6> x abs_path('../testing/') # También con directorios 0 '/home/pp2/src/perl/testing'
Otra forma de manipular un directorio es mediante el uso de un directory handle o manejador de directorio. Podemos abrirlo con opendir, leer del mismo el siguiente nombre de fichero mediante readdir y cerrarlo con closedir.
lhp@nereida:~/Lperl/src/dir$ cat -n dirhandles.pl 1 #!/usr/bin/perl -w 2 use strict; 3 4 my $directory = shift; 5 my $regexp = (shift or '.*'); 6 opendir DH, $directory or die "No encuentro $directory: $!"; 7 8 foreach my $file (readdir DH) { 9 printf "$file\n" if $file =~ m/$regexp/o; 10 } 11 closedir DH;Sigue un ejemplo de ejecución:
lhp@nereida:~/Lperl/src/dir$ dirhandles.pl /tmp/ '\d+$' ssh-eSbkNq1402 v274545 Parallel-Simple-Pipe-0.01 IO-Tty-1.02
readdir
no contienen
el camino, sólo el nombre.
La tabla 2.3 recoge de manera sucinta algunos otros operadores para la manipulación de ficheros y directorios.
Los módulos File::Basename y File::Spec permiten el manejo de nombres de fichero de una manera portable. Veamos por ejemplo la función catfile:
lhp@nereida:~/Lperl/src/perl_networking/ch2$ perl -de 0 DB<1> use File::Spec::Functions; DB<2> $x = catfile('a', 'b', 'c') DB<3> x $x 0 'a/b/c'En un sistema windows
$x
contendría 'a\b\c'
.
Para saber mas, lea con detalle la documentación
de los módulos File::Basename y File::Spec.
Usa para ello el comando man File::Basename
o bien
perldoc File::Basename
.
Sigue un ejemplo de uso de la función fileparse:
DB<1> use File::Basename DB<2> @a = fileparse("/foo/bar/baz", qr/\.[^.]*/) DB<3> x @a 0 'baz' 1 '/foo/bar/' 2 '' DB<4> @a = fileparse("/foo/bar/baz.pm", qr/\.[^.]*/) DB<5> x @a 0 'baz' 1 '/foo/bar/' 2 '.pm'
El operador glob permite la expansión de los comodines de la shell. Asi en una expresión como:
my @all_files = glob "*";la variable
all_files
guarda ordenados
alfabéticamente la lista de los nombres de los ficheros en el directorio actual.
Es posible, como indica el siguiente ejemplo utilizar varios patrones separados por espacios.
my @including_hidden = glob ".* *";
Usar glob
es mas eficiente y mas portable que enviar el requerimiento a la
shell del sistema usando backticks (como por ejemplo en `ls .*`
).
Cuando en el operador de lectura <>
el argumento es una cadena glob
de descripción de ficheros con comodines, el globbing se produce automáticamente:
my $dir = "/home/casiano/"; my $dir_files = <$dir/* $dir/.*>;
También puede hacerse en un contexto de lista:
DB<1> @modules = <*.pm> DB<2> p "@modules" A.pm Abstract.pm B.pm C.pm DandC.pm Frac.pm Fraction.pm
En general, si el identificador entre ángulos es un filehandler, Perl hace un acceso a través de él, en caso contrario interpreta que se trata de una operación de globbing. Esto es asi, incluso si la lectura hace uso de un manipulador indirecto que use variables intermedias. Véase el siguiente ejemplo:
DB<1> $f = 'yapp' DB<2> @f = <$f/*.yp> DB<3> p "@f" yapp/aSb.yp yapp/Autoaction1.yp yapp/Calc.yp yapp/Calc2.yp DB<4> open F, 'logic.pl' DB<5> $x = F DB<6> @x = <$x> DB<7> p "@x" #!/usr/bin/perl -w $a = 4; $b = "hola"; $c = 0; $d = ""; print $a && $b,"\n"; print $a and $b,"\n"; print ($a and $b),"\n";
lhp@nereida:~/alu/0607$ perl -wde 0 main::(-e:1): 0 DB<1> use List::Util qw(sum) DB<2> p sum(map { -s $_ } <alu*>) 163840En este código, la lectura
<alu*>
se produce en un
contexto de lista. Por ello el directorio completo es leído y produce una lista con todos los
nombres de ficheros y directorios alu*
existentes en el directorio actual. El subsecuente
map
produce los tamaños y la llamada a sum
devuelve el tamaño total.
En ocasiones es importante distinguir entre falsedad e indefinición, como en este ejemplo:
while ($file = <*>) { do_something($file); }En este código, en cada pasada del bucle el operador
<*>
produce
un nuevo nombre de fichero del directorio actual. El nombre es asignado a
$file
. ¿Que ocurre si el nombre del fichero es "0"
?.
En tal caso el bucle debería tener una ''muerte'' prematura. La codificación
correcta sería:
while (defined($file = <*>) { do_something($file); }
Siguiendo la filosofía de ''ayudar'' al programador, las últimas
versiones de Perl tiene el comportamiento ''esperado''. Esto es, detectan el uso
en un while
del glob
e interpretan que "0"
como nombre de fichero no
es falso. Sin embargo en otro contexto, "0"
si se interpreta como falso. Observe
el siguiente ejemplo:
$ cat muerte_prematura2.pl #!/usr/bin/perl -w my $file; my @a = ("uno","0","dos"); while ($file = shift @a) { do_something($file); } sub do_something { print "$file\n"; }Al ejecutar obtenemos:
$ ./muerte_prematura2.pl uno
Esta modificación de la evaluación ''normal'' de una expresión en un contexto lógico/booleano no es (del todo) privilegio exclusivo de Perl. El programador puede conseguir un efecto similar usando el módulo Contextual::Return de Damian Conway, el cual permite crear variables contextuales multitipo con mas de dos tipos:
lhp@nereida:~/Lperl/src/testing$ cat -n context1.pl 1 #!/usr/local/bin/perl -w 2 use strict; 3 use Contextual::Return; 4 5 my $x = BOOL { 0 } NUM { 3.14 } STR { "pi" }; 6 7 unless ($x) { warn "¡El famoso número $x (".(0+$x).") pasa a ser falso!\n" } # executed! lhp@nereida:~/Lperl/src/testing$ context1.pl ¡El famoso número pi (3.14) pasa a ser falso!