• Publicidad

Distribuir archivos en carpetas

¿Apenas comienzas con Perl? En este foro podrás encontrar y hacer preguntas básicas de Perl con respuestas aptas a tu nivel.

Distribuir archivos en carpetas

Notapor sisifo80 » 2015-01-16 11:50 @535

Hola,

Estoy intentando hacer un script que distribuya archivos en carpetas en función del contenido del archivo. En concreto, tengo un conjunto de archivos .xml que contienen el elemento <year></year>. Dentro de este elemento puede haber cuatro opciones:
Sintáxis: [ Descargar ] [ Ocultar ]
Using xml Syntax Highlighting
  1. <year>1500</year>
  2. <year>1600</year>
  3. <year>1700</year>
  4. <year>1800</year>
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Lo que quiero es distribuir los archivos .xml en cuatro carpetas (1500, 1600, 1700, 1800) en función del contenido del elemento year. Por ejemplo, si el archivo .xml tiene el elemento <year>1500</year> ese archivo debe ir a la carpeta 1500, y así sucesivamente. Los archivos están todos en una carpeta llamada "input".

He probado a hacer lo siguiente:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. mkdir '1500';
  2. mkdir '1600';
  3. mkdir '1700';
  4. mkdir '1800';
  5.  
  6. opendir IN, 'input';
  7. my @in = grep { /^[^.]/ } readdir IN;
  8. closedir IN;
  9.  
  10. for my $in (@in) {
  11.   open IN, '<', "input/$in";
  12.   $/ = undef;
  13.  
  14.   $file = <IN>;
  15.  
  16.   if ($file =~ /<year>15/) {
  17.        
  18.   rename "$in", "1500";
  19.   }
  20.  
  21.   }
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4

Consigo crear las carpetas, pero no consigo crear una copia de cada archivo en la carpeta correspondiente. ¿Alguna sugerencia?
Última edición por explorer el 2015-01-16 12:29 @562, editado 1 vez en total
Razón: Interrogantes
sisifo80
Perlero nuevo
Perlero nuevo
 
Mensajes: 32
Registrado: 2013-11-20 07:30 @354

Publicidad

Re: Distribuir archivos en carpetas

Notapor explorer » 2015-01-16 13:18 @596

El problema con este enfoque es que si, en el futuro, aparece otro valor de year, tendrás que reeditar el programa.

Lo mejor es que el programa sea lo más robusto posible, es decir: que sea capaz de adaptarse a los cambios.

Ya que la información de en qué carpeta debe almacenarse el archivo está en el propio archivo, podemos aprovecharnos de eso (no probado):
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. opendir IN, 'input';
  3. while (my $in = readdir IN) {
  4.     next if $in =~ /^[.]/;                      # pasa al siguiente si $in comienza por '.'
  5.  
  6.     open ARCHIVO, '<', "input/$in";
  7.     $/ = undef;
  8.     my $file = <ARCHIVO>;
  9.     close ARCHIVO;
  10.  
  11.     if ($file =~ m{<year>(.+?)</year>}) {
  12.         my $year = $1;
  13.  
  14.         if (! -d $year) {                       # si no existe carpeta de ese año
  15.             mkdir $year;                        # la creamos
  16.         }
  17.  
  18.         rename "input/$in", "$year/$in";        # lo movemos
  19.     }
  20. }
  21. closedir IN;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Fíjate que en el rename() tenemos que dar la ruta completa de dónde a dónde.

Además... si en el archivo no existe una marca <year>, no intenta el movimiento, pues suponemos que no es un archivo de los que nos interesa.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14476
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Distribuir archivos en carpetas

Notapor sisifo80 » 2015-01-17 05:43 @280

Gracias por tu respuesta, explorer.

El problema es que me expresé mal en mi mensaje. En realidad, el elemento <year></year> no tiene cuatro opciones, tiene múltiples opciones. Por ejemplo:

1) <year>1765</year>
2) <year>1503</year>
3) <year>1654</year>
....

Se trata de distribuir los archivos en función del siglo y, por tanto, en función de los dos primeros dígitos. Por ejemplo, el caso 1 debería aparecer en la carpeta llamada 1700, el caso 2 en la carpeta 1500, etc.

A partir de tu sugerencia, creé el siguiente script, que sí funciona:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. mkdir "1500";
  3. mkdir "1600";
  4. mkdir "1700";
  5. mkdir "1800";
  6.  
  7. opendir IN, 'input';
  8. while ( my $in = readdir IN ) {
  9.     next if $in =~ /^[.]/;
  10.  
  11.     open ARCHIVO, '<', "input/$in";
  12.     $/ = undef;
  13.     my $file = <ARCHIVO>;
  14.     close ARCHIVO;
  15.  
  16.     if ( $file =~ m{<year>15} ) {
  17.  
  18.         rename "input/$in", "1500/$in";
  19.     }
  20.  
  21.     if ( $file =~ m{<year>16} ) {
  22.  
  23.         rename "input/$in", "1600/$in";
  24.     }
  25.  
  26.     if ( $file =~ m{<year>17} ) {
  27.  
  28.         rename "input/$in", "1700/$in";
  29.     }
  30.  
  31.     if ( $file =~ m{<year>18} ) {
  32.  
  33.         rename "input/$in", "1800/$in";
  34.     }
  35. }
  36. closedir IN;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


No obstante, todavía tengo algunas dudas:

1) Compruebo que la orden "rename" elimina el archivo de su ubicación original (en mi caso la carpeta "input"). ¿Existe alguna opción para que el archivo no se traslade sino que simplemente se copie?

2) Cuál es la finalidad que está detrás de la orden
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. next if $in =~ /^[.]/;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


3) Habría alguna forma de hacer más robusta mi propuesta.

Gracias de nuevo por tus sugerencias, con las que siempre aprendo.
sisifo80
Perlero nuevo
Perlero nuevo
 
Mensajes: 32
Registrado: 2013-11-20 07:30 @354

Re: Distribuir archivos en carpetas

Notapor sisifo80 » 2015-01-17 06:00 @292

Ya me respondo yo a la primera de mis dudas :)

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. use File::Copy;
  2.  
  3. mkdir "1500";
  4. mkdir "1600";
  5. mkdir "1700";
  6. mkdir "1800";
  7.  
  8. opendir IN, 'input';
  9. while ( my $in = readdir IN ) {
  10.     next if $in =~ /^[.]/;
  11.  
  12.     open ARCHIVO, '<', "input/$in";
  13.     $/ = undef;
  14.     my $file = <ARCHIVO>;
  15.     close ARCHIVO;
  16.  
  17.     if ( $file =~ m{<year>15} ) {
  18.  
  19.         copy( "input/$in", "1500/$in" );
  20.     }
  21.  
  22.     if ( $file =~ m{<year>16} ) {
  23.  
  24.         copy( "input/$in", "1600/$in" );
  25.     }
  26.  
  27.     if ( $file =~ m{<year>17} ) {
  28.  
  29.         copy( "input/$in", "1700/$in" );
  30.     }
  31.  
  32.     if ( $file =~ m{<year>18} ) {
  33.  
  34.         copy( "input/$in", "1800/$in" );
  35.     }
  36. }
  37. closedir IN;
  38.  
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
Última edición por explorer el 2015-01-17 09:42 @446, editado 1 vez en total
Razón: Formateado de código con Perltidy;
sisifo80
Perlero nuevo
Perlero nuevo
 
Mensajes: 32
Registrado: 2013-11-20 07:30 @354

Re: Distribuir archivos en carpetas

Notapor explorer » 2015-01-17 10:46 @490

1) la línea hace la mismo que el grep() que tenías en el primer código: filtrar aquellos archivos que no empiecen por '.'

2) para hacerlo más robusto lo que hay que hacer es tener que evitar que crear y copiar archivos a nombres de carpetas fijos en el programa.

Esta es otra versión, más robusta (pero no probada):
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. use File::Copy;
  3.  
  4. $/ = undef;
  5.  
  6. opendir IN, 'input';
  7.  
  8. while ( my $in = readdir IN ) {
  9.     next if $in =~ /^[.]/;             # pasa al siguiente si $in comienza por '.'
  10.  
  11.     open ARCHIVO, '<', "input/$in";
  12.     my $file = <ARCHIVO>;
  13.     close ARCHIVO;
  14.  
  15.     if ( $file =~ m{<year>(.+?)</year>} ) {
  16.         my $siglo = substr $1, 0, 2;          # los dos primeros caracteres
  17.  
  18.         mkdir $siglo if ! -d $siglo;          # si no existe carpeta de ese año
  19.  
  20.         copy( "input/$in", "$siglo/$in" );    # lo copiamos
  21.     }
  22. }
  23.  
  24. closedir IN;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Como ves, se trata de extraer el número de siglo desde el contenido del propio valor de la etiqueta <year>; crear la carpeta del siglo si no existe; y hacer la copia.

Hay otra forma... si hemos leído todo el archivo, no necesitamos hacer una copia: basta con abrir un nuevo archivo destino y grabar en él el archivo leído:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. $/ = undef;
  3.  
  4. opendir IN, 'input';
  5.  
  6. while ( my $in = readdir IN ) {
  7.     next if $in =~ /^[.]/;                  # pasa al siguiente si $in comienza por '.'
  8.  
  9.     open     my $ARCHIVO, '<', "input/$in";
  10.     my $file = <$ARCHIVO>;
  11.     close       $ARCHIVO;
  12.  
  13.     if ($file =~ m{<year>(..)..</year>}) {
  14.         my $siglo = $1;                     # los dos primeros caracteres son el siglo
  15.  
  16.         mkdir $siglo if ! -d $siglo;        # si no existe carpeta de ese año
  17.  
  18.         open my $OUT, '>', "$siglo/$in";    # lo copiamos
  19.         print   $OUT $file;
  20.         close   $OUT;
  21.     }
  22. }
  23.  
  24. closedir IN;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Queda un poco más corto :)

De todas maneras, hay un error de concepto: Los siglos no pueden tomarse de los dos primeros caracteres del año.

Un siglo va desde el año 1 hasta el año 100.

Por ejemplo, el siglo XX va desde el 1901 hasta el 2000. Y el XXI va desde el 2001 al 2100.

Esto quedaría mejor así:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. $/ = undef;
  3.  
  4. opendir my $DIR, 'input';
  5.  
  6. while (  my $in = readdir $DIR ) {
  7.     next if $in =~ /^[.]/;             # pasa al siguiente si $in comienza por '.'
  8.  
  9.     open     my $ARCHIVO, '<', "input/$in";
  10.     my $file = <$ARCHIVO>;
  11.     close       $ARCHIVO;
  12.  
  13.     next if $file !~ m{<year>(..)(..)</year>};         # pasamos al siguiente si $file no contiene <year>....</year>
  14.  
  15.     my($siglo,$unidades) = ($1,$2);
  16.  
  17.     $siglo-- if $unidades == 0;                        # si el año es 00, pertenece al siglo anterior
  18.  
  19.     mkdir $siglo if ! -d $siglo;                       # crear carpeta si no existe carpeta de ese siglo
  20.  
  21.     open my $OUT, '>', "$siglo/$in";                   # lo copiamos allí
  22.     print   $OUT $file;
  23.     close   $OUT;
  24. }
  25.  
  26. closedir $DIR;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
Pero, bueno, quizás sea demasiado estricto.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14476
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Distribuir archivos en carpetas

Notapor sisifo80 » 2015-01-18 05:29 @270

Sí, tienes toda la razón. Soy consciente del error de concepto con los límites del siglo, aunque responde a decisiones internas en el proyecto en el que trabajo.

Te agradezco mucho tus sugerencias y las distintas opciones que me facilitas. Me sirven de mucho para ir aprendiendo nuevas expresiones en perl. Gracias.
sisifo80
Perlero nuevo
Perlero nuevo
 
Mensajes: 32
Registrado: 2013-11-20 07:30 @354


Volver a Básico

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 10 invitados

cron