複数のファイルを含むフォルダーがあります。これらのファイルは または.xml
ファイル.zip
です。これらの.zip
ファイルには.xml
and/or.zip
ファイルが含まれています。これらにはまたは.zip
も含まれており、最終的にファイルが見つかるまでこれを繰り返します。.xml
.zip
.xml
言い換えれば、.xml
ファイルを見つける前に、いくつかの「レベル」の zip が存在する可能性があります (以下の例を参照)。
私の要求は、根ZIP ファイルには、100 MB を超える XML ファイルが少なくとも 1 つ含まれています。ZIP ファイルがこのような場合は、別のディレクトリ ( など~/big-files
) に移動する必要があります。また、非圧縮ファイルが 100 MB を超える場合は.xml
、このディレクトリに移動する必要があります。
例えば:
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
この例では、ドキュメント、1.zip、2.zipそして3.zip~/big-files
100MBを超えるXMLファイルを少なくとも1つホストしているため、4.zip。
bash シェルでこれを実現するにはどうすればよいですか?
ありがとう。
答え1
まずインストールAVFSは、アーカイブ内で透過的なアクセスを提供するファイルシステムであり、コマンドを実行しますmountavfs
。圧縮されたアーカイブを再帰的に grep するにはどうすればいいですか?背景用。
この後、/path/to/archive.zip
が認識されたアーカイブである場合、 は~/.avfs/path/to/archive.zip#
アーカイブの内容を含むと思われるディレクトリです。
引数として渡された zip ファイル内の大きな XML ファイルを検索し、埋め込まれたすべての zip ファイルに対して自分自身を再帰的に呼び出す、という補助スクリプトを作成しますhas_large_file_rec
。このスクリプトは、内部に大きな XML ファイルを見つけた場合に何らかの出力を生成します。出力は効率化のために切り捨てられます。大きな XML ファイルが 1 つ見つかったら、検索を停止したほうがよいためです。
#!/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
を使用する 1 つの方法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 MB より大きいサイズがないかチェックします。 zip ファイルを抽出するたびにチェックする方が高速です。 改善の余地があります。 - 複数回処理された zip ファイルはキャッシュされません。
~/big_files
存在し、書き込み可能である必要があります。zip
スクリプトは引数を受け入れません。およびファイルと同じディレクトリで実行する必要がありますxml
。
前のポイントでわかるように完璧ではありませんが、私のテストではうまくいきました。皆さんのお役に立てれば幸いです。
次のように実行します:
perl script.pl