Tengo una carpeta con varios archivos. Estos archivos son archivos .xml
o .zip
. Estos .zip
archivos contienen .xml
y/o .zip
archivos. Estos .zip
también contienen .xml
o .zip
, y así sucesivamente... hasta que finalmente encontramos .xml
archivos.
En otras palabras, puedo tener varios "niveles" de zip antes de encontrar mis .xml
archivos (ver ejemplo a continuación).
Mi requisito es detectar cuálraízLos archivos ZIP contienen al menos un archivo XML de más de 100 Mb. Cuando un archivo ZIP se encuentra en tal caso, debe moverse a otro directorio (digamos ~/big-files
). Además, si un .xml
archivo no comprimido pesa más de 100 Mb, debe moverse a este directorio.
Por ejemplo:
foo1.xml
foo2.xml
baz.xml [MORE THAN 100Mb]
one.zip
+- foo.xml
+- bar.xml [MORE THAN 100Mb]
+- foo.xml
two.zip
+- foo.xml
+- zip-inside1.zip
| +- bar.xml [MORE THAN 100Mb]
+- foo.xml
three.zip
+- foo.xml
+- zip-inside2.zip
| +- zip-inside3.zip
| +- foo.xml
| +- bar.xml [MORE THAN 100Mb]
+- foo.xml
four.zip
+- foo.xml
+- zip-inside1.zip
+- foo.xml
En este ejemplo,baz.xml,uno.zip,dos.zipytres.zipdeben trasladarse ~/big-files
ya que alojan al menos un archivo XML de más de 100 Mb, pero nocuatro.zip.
¿Cómo puedo lograr eso en bash shell?
Gracias.
Respuesta1
Primero, instaleAVFS, un sistema de archivos que proporciona acceso transparente dentro de los archivos, y ejecute el comando mountavfs
. Ver¿Cómo puedo buscar de forma recursiva en archivos comprimidos?para fondo.
Después de esto, si /path/to/archive.zip
es un archivo reconocido, entonces ~/.avfs/path/to/archive.zip#
hay un directorio que parece contener el contenido del archivo.
Escriba un script auxiliar llamado has_large_file_rec
que busque un archivo XML grande en el archivo zip pasado como argumento y se llame a sí mismo de forma recursiva en cada archivo zip incrustado. Este script produce algún resultado si encuentra un archivo XML grande en su interior. La salida se trunca para mayor eficiencia, ya que una vez que hayamos encontrado un archivo XML grande, también podríamos dejar de buscar.
#!/bin/sh
## auxiliary script has_large_file_rec
find "$1#" -name '*.zip' -type f -exec has_large_file_rec {} \; \
-o -name '*.xml' -type f -size +1024k -print | head -n 1
En el nivel superior, si encuentra un archivo grande, muévalo al directorio de archivos grandes.
find "~/.avfs$PWD" \
-name '*.zip' -sh -c '
a=$(has_large_file_rec "$0")
if [ -n "$a" ]; then mv "$0" ~/big-files/; fi
' {} \; -o \
-name '*.xml' -type f -size +1024k -exec mv {} ~/big-files/ \;
Respuesta2
Uso unidireccional perl
.
Contenido de script.pl
:
use warnings;
use strict;
use Archive::Extract;
use List::Util qw|first|;
use File::Copy qw|move|;
use File::Spec;
use File::Path qw|remove_tree|;
## Path to save 'xml' and 'zip' files.
my $big_files_dir = qq|$ENV{HOME}/big_files/|;
## Temp dir to extract files of 'zips'.
my $zips_path = qq|/tmp/zips$$/|;
## Size in bytes to check 'xml' files.
my $file_max_size_bytes = 100 * 1024 * 1024;
my (@zips_to_move, $orig_zip);
## Get files to process.
my @files = <*.xml *.zip>;
## From previous list, copy 'xml' files bigger than size limit.
for my $file ( @files ) {
if ( substr( $file, -4 ) eq q|.xml| and -s $file > $file_max_size_bytes ) {
move $file, $big_files_dir;
}
}
## Process now 'zip' files. For each one remove temp dir to avoid mixing files
## from different 'zip' files.
for ( grep { m/\.zip\Z/ } @files ) {
remove_tree $zips_path;
$orig_zip = $_;
handle_zip_file( $orig_zip );
}
## Copy 'zip' files got until now.
for my $zip_file ( @zips_to_move ) {
move $zip_file, $big_files_dir;
}
## Traverse recursively each 'zip file. It will look for 'zip' file in the
## subtree and will extract all 'xml' files to a temp dir. Base case is when
## a 'zip' file only contains 'xml' files, then I will read size of all 'xmls'
## and will copy the 'zip' if at least one of them if bigger than the size limit.
## To avoid an infinite loop searching into 'zip' files, I delete them just after
## the extraction of its content.
sub handle_zip_file {
my ($file) = @_;
my $ae = Archive::Extract->new(
archive => $file,
type => q|zip|,
);
$ae->extract(
to => $zips_path,
);
## Don't check fails. I don't worry about them, ¿perhaps should I?
unlink( File::Spec->catfile(
(File::Spec->splitpath( $zips_path ))[1],
(File::Spec->splitpath( $file ))[2],
)
);
my $zip = first { substr( $_, -4 ) eq q|.zip| } <$zips_path/*>;
if ( ! $zip ) {
for my $f ( <$zips_path/*.xml> ) {
if ( substr( $f, -4 ) eq q|.xml| and -s $f > $file_max_size_bytes ) {
push @zips_to_move, $orig_zip;
last;
}
}
return;
}
handle_zip_file( $zip );
}
Algunos asuntos:
xml
Los archivos con el mismo nombre en el subárbol de unzip
archivo se sobrescribirán cuando se copien al directorio temporal.- Este programa extrae el contenido de todos los archivos zip del mismo árbol y luego busca un
xml
tamaño superior a 100 MB. Sería más rápido comprobarlo cada vez que extraigo un archivo zip. Se puede mejorar. - No almacena en caché los archivos zip procesados más de una vez.
~/big_files
debe existir y poder escribirse.- El guión no acepta argumentos. Debe ejecutarlo en el mismo directorio que los archivos
zip
yxml
.
No es perfecto como puedes ver en puntos anteriores pero funcionó en mi prueba. Espero que pueda serte útil.
Ejecútelo como:
perl script.pl