Поиск файлов определенного размера внутри рекурсивных сжатых архивов

Поиск файлов определенного размера внутри рекурсивных сжатых архивов

У меня есть папка с несколькими файлами. Эти файлы являются либо файлами .xml, либо .zipфайлами. Эти .zipфайлы содержат .xmlи/или .zipфайлы. Эти .zipсодержат также .xmlили .zip, и так далее... пока мы, наконец, не нашли .xmlфайлы.

Другими словами, я могу иметь несколько «уровней» zip-архива, прежде чем найду свои .xmlфайлы (см. пример ниже).

Мое требование — определить, какойкореньZIP-файлы содержат по крайней мере один XML-файл, который больше 100 Мб. Когда ZIP-файл находится в таком случае, его следует переместить в другой каталог (скажем, ~/big-files). Также, если не заархивированный .xmlфайл больше 100 Мб, его следует переместить в этот каталог.

Например:

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

В этом примеребаз.xml,один.zip,два.zipитри.zipследует переместить на , ~/big-filesпоскольку они размещают по крайней мере один XML-файл размером более 100 МБ, но нечетыре.zip.

Как этого добиться в оболочке bash?

Спасибо.

решение1

Сначала установитеАВФС, файловая система, которая обеспечивает прозрачный доступ внутри архивов, и выполните команду mountavfs. СмотритеКак выполнить рекурсивный grep по сжатым архивам?для фона.

После этого, если /path/to/archive.zipэто распознанный архив, то ~/.avfs/path/to/archive.zip#это каталог, который, по-видимому, содержит содержимое архива.

Напишите вспомогательный скрипт, который называется , has_large_file_recкоторый ищет большой XML-файл в zip-файле, переданном в качестве аргумента, и вызывает себя рекурсивно для каждого встроенного zip-файла. Этот скрипт выдает какой-то вывод, если находит внутри большой XML-файл. Вывод усекается для эффективности, поскольку как только мы находим один большой XML-файл, мы можем прекратить поиск.

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

Если на верхнем уровне вы найдете большой файл, переместите его в каталог больших файлов.

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/ \;

решение2

Один из способов использования perl.

Содержание 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 );
}

Некоторые вопросы:

  • xmlФайлы с одинаковыми именами в поддереве файла zipбудут перезаписаны при копировании во временный каталог.
  • Эта программа извлекает содержимое всех zip-файлов одного дерева, а затем проверяет, xmlбольше ли 100 МБ. Было бы быстрее проверять каждый раз, когда я извлекаю zip-файл. Это можно улучшить.
  • Он не кэширует zip-файлы, обработанные более одного раза.
  • ~/big_filesдолжен существовать и быть доступным для записи.
  • Скрипт не принимает аргументы. Вы должны запустить его в том же каталоге, что zipи xmlфайлы и.

Это не идеально, как вы можете видеть в предыдущих пунктах, но это сработало в моем тесте. Надеюсь, это может быть полезно для вас.

Запустите его так:

perl script.pl

Связанный контент