Procura por arquivos com um tamanho específico dentro de arquivos zipados recursivos

4

Eu tenho uma pasta com vários arquivos. Esses arquivos são .xml ou .zip files. Esses arquivos .zip contêm arquivos .xml e / ou .zip . Esses .zip também contêm .xml ou .zip e assim por diante ... até que finalmente encontramos .xml arquivos.

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

Meu requisito é detectar quais arquivos ZIP root contêm pelo menos um arquivo XML maior que 100Mb. Quando um arquivo ZIP estiver nesse caso, ele deve ser movido para outro diretório (digamos ~/big-files ). Além disso, se um arquivo .xml não zipado for maior que 100Mb, 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 , one.zip , two.zip e three.zip deve ser movido para ~/big-files , pois eles hospedam pelo menos um arquivo XML maior que 100Mb, mas não four.zip .

Como posso conseguir isso no bash shell?

Obrigado.

    
por Romain Linsolas 06.07.2012 / 16:06

2 respostas

2

Primeiro, instale o AVFS , um sistema de arquivos que fornece acesso transparente dentro dos arquivos e execute o comando mountavfs . Consulte Como faço para percorrer recursivamente arquivos compactados? para o fundo.

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

Escreva um script auxiliar chamado has_large_file_rec que procura um arquivo XML grande no arquivo zip passado como argumento e chama-se 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 eficiência, uma vez que, depois de encontrarmos um arquivo XML grande, poderíamos 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 grande.

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/ \;
    
por 07.07.2012 / 02:06
1

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 );
}

Algumas questões:

  • xml arquivos com o mesmo nome na subárvore de um arquivo zip serão sobrescritos quando copiados para a pasta temporária.
  • Este programa extrai o conteúdo de todos os arquivos zip da mesma árvore e Em seguida, verifica se há xml maior que 100MB. Seria mais rápido verifique cada vez que extraio um arquivo zip. Pode ser melhorado.
  • Ele não armazena em cache arquivos zip processados mais de uma vez.
  • ~/big_files deve existir e ser gravável.
  • O script não aceita argumentos. Você deve executá-lo no mesmo diretório como os arquivos zip e 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
    
por 07.07.2012 / 02:13