Eu tenho uma pasta com vários arquivos. Esses arquivos são .xml
ou .zip
arquivos. Esses .zip
arquivos contêm .xml
e/ou .zip
arquivos. Estes .zip
também contêm .xml
ou .zip
e assim por diante... até que finalmente encontramos .xml
os arquivos.
Em outras palavras, posso ter vários "níveis" de zip antes de encontrar meus .xml
arquivos (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 .xml
for 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-files
porque 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.zip
for 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_rec
que 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:
xml
arquivos com o mesmo nome na subárvore de umzip
arquivo 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á
xml
mais 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_files
deve existir e ser gravável.- O script não aceita argumentos. Você deve executá-lo no mesmo diretório dos arquivos
zip
exml
.
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