Procure arquivos com um tamanho específico dentro de arquivos compactados recursivamente

Procure arquivos com um tamanho específico dentro de arquivos compactados recursivamente

Eu tenho uma pasta com vários arquivos. Esses arquivos são .xmlou .ziparquivos. Esses .ziparquivos contêm .xmle/ou .ziparquivos. Estes .ziptambém contêm .xmlou .zipe assim por diante... até que finalmente encontramos .xmlos arquivos.

Em outras palavras, posso ter vários "níveis" de zip antes de encontrar meus .xmlarquivos (cf. exemplo abaixo).

Minha exigência é detectar qualraizOs arquivos ZIP contêm pelo menos um arquivo XML maior que 100 MB. Quando um arquivo ZIP estiver nesse caso, ele deve ser movido para outro diretório (digamos ~/big-files). Além disso, se um arquivo não compactado .xmlfor maior que 100 MB, ele deverá ser movido para este diretório.

Por exemplo:

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

Neste exemplo,baz.xml,um.zip,dois.zipetrês.zipdevem ser movidos ~/big-filesporque hospedam pelo menos um arquivo XML maior que 100 MB, mas nãoquatro.zip.

Como posso conseguir isso no bash shell?

Obrigado.

Responder1

Primeiro, instaleAVFS, um sistema de arquivos que fornece acesso transparente dentro dos arquivos, e execute o comando mountavfs. VerComo faço para percorrer recursivamente arquivos compactados?para plano de fundo.

Depois disso, se /path/to/archive.zipfor um arquivo reconhecido, então ~/.avfs/path/to/archive.zip#será um diretório que parece conter o conteúdo do arquivo.

Escreva um script auxiliar chamado has_large_file_recque procure um arquivo XML grande no arquivo zip passado como argumento e se chame recursivamente em cada arquivo zip incorporado. Este script produz alguma saída se encontrar um arquivo XML grande dentro dele. A saída é truncada para maior eficiência, pois assim que encontrarmos um arquivo XML grande, poderemos parar de pesquisar.

#!/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

No nível superior, se você encontrar um arquivo grande, mova-o para o diretório de arquivos 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/ \;

Responder2

Uma maneira de usar perl.

Conteúdo 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 );
}

Alguns problemas:

  • xmlarquivos com o mesmo nome na subárvore de um ziparquivo serão substituídos quando copiados para o diretório temporário.
  • Este programa extrai o conteúdo de todos os arquivos zip da mesma árvore e depois verifica se há xmlmais de 100 MB. Seria mais rápido verificar cada vez que extraio um arquivo zip. Pode ser melhorado.
  • Ele não armazena em cache arquivos zip processados ​​mais de uma vez.
  • ~/big_filesdeve existir e ser gravável.
  • O script não aceita argumentos. Você deve executá-lo no mesmo diretório dos arquivos zipe xml.

Não é perfeito como você pode ver nos pontos anteriores, mas funcionou no meu teste. Espero que possa ser útil para você.

Execute como:

perl script.pl

informação relacionada