Suche nach Dateien mit einer bestimmten Größe in rekursiven ZIP-Archiven

Suche nach Dateien mit einer bestimmten Größe in rekursiven ZIP-Archiven

Ich habe einen Ordner mit mehreren Dateien. Diese Dateien sind entweder .xmloder .zipDateien. Diese .zipDateien enthalten .xmlund/oder .zipDateien. Diese .zipenthalten auch .xmloder .zipund so weiter... bis wir endlich .xmlDateien gefunden haben.

Mit anderen Worten, ich kann mehrere Zip-„Ebenen“ haben, bevor ich meine .xmlDateien finde (vgl. Beispiel unten).

Meine Anforderung besteht darin, herauszufinden, welcheWurzelZIP-Dateien enthalten mindestens eine XML-Datei, die größer als 100 MB ist. Wenn dies bei einer ZIP-Datei der Fall ist, sollte sie in ein anderes Verzeichnis verschoben werden (sagen wir ~/big-files). Wenn eine nicht komprimierte .xmlDatei größer als 100 MB ist, sollte sie ebenfalls in dieses Verzeichnis verschoben werden.

Zum Beispiel:

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

In diesem Beispielbaz.xml,eins.zip,zwei.zipUnddrei.zipsollte verschoben werden, ~/big-filesda sie mindestens eine XML-Datei größer als 100 MB hosten, aber nicht4.zip.

Wie kann ich das in der Bash-Shell erreichen?

Danke.

Antwort1

Installieren Sie zunächstAVFS, ein Dateisystem, das transparenten Zugriff innerhalb von Archiven ermöglicht, und führen Sie den Befehl aus mountavfs. SieheWie durchsuche ich komprimierte Archive rekursiv?für den Hintergrund.

Wenn es sich danach /path/to/archive.zipum ein erkanntes Archiv handelt, ~/.avfs/path/to/archive.zip#folgt ein Verzeichnis, das scheinbar den Inhalt des Archivs enthält.

Schreiben Sie ein Hilfsskript namens has_large_file_rec, das in der als Argument übergebenen Zip-Datei nach einer großen XML-Datei sucht und sich selbst rekursiv für jede eingebettete Zip-Datei aufruft. Dieses Skript erzeugt eine Ausgabe, wenn es darin eine große XML-Datei findet. Die Ausgabe wird aus Effizienzgründen gekürzt, da wir die Suche auch gleich beenden können, sobald wir eine große XML-Datei gefunden haben.

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

Wenn Sie auf der obersten Ebene eine große Datei finden, verschieben Sie sie in das Verzeichnis für große Dateien.

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

Antwort2

Eine Möglichkeit ist die Verwendung von perl.

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

Einige Fragen:

  • xmlDateien mit demselben Namen im Unterbaum einer zipDatei werden beim Kopieren in das temporäre Verzeichnis überschrieben.
  • Dieses Programm extrahiert den Inhalt aller Zip-Dateien desselben Baums und prüft dann, ob sie xmlgrößer als 100 MB sind. Es wäre schneller, wenn ich jedes Mal, wenn ich eine Zip-Datei extrahiere, eine Prüfung durchführen würde. Das kann verbessert werden.
  • ZIP-Dateien, die mehr als einmal verarbeitet werden, werden nicht im Cache gespeichert.
  • ~/big_filesmuss vorhanden und beschreibbar sein.
  • Das Skript akzeptiert keine Argumente. Sie müssen es im selben Verzeichnis wie die zipund xml-Dateien ausführen.

Es ist nicht perfekt, wie Sie in den vorherigen Punkten sehen können, aber es hat bei meinem Test funktioniert. Ich hoffe, es kann Ihnen nützlich sein.

Führen Sie es wie folgt aus:

perl script.pl

verwandte Informationen