Busque archivos con un tamaño específico dentro de archivos comprimidos recursivos

Busque archivos con un tamaño específico dentro de archivos comprimidos recursivos

Tengo una carpeta con varios archivos. Estos archivos son archivos .xmlo .zip. Estos .ziparchivos contienen .xmly/o .ziparchivos. Estos .ziptambién contienen .xmlo .zip, y así sucesivamente... hasta que finalmente encontramos .xmlarchivos.

En otras palabras, puedo tener varios "niveles" de zip antes de encontrar mis .xmlarchivos (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 .xmlarchivo 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-filesya 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.zipes 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_recque 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:

  • xmlLos archivos con el mismo nombre en el subárbol de un ziparchivo 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 xmltamañ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_filesdebe existir y poder escribirse.
  • El guión no acepta argumentos. Debe ejecutarlo en el mismo directorio que los archivos zipy xml.

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

información relacionada