我有一個包含多個文件的資料夾。這些文件是.xml
或.zip
文件。這些.zip
文件包含.xml
和/或.zip
文件。這些.zip
也包含.xml
或.zip
,依此類推...直到我們最終找到.xml
文件。
換句話說,在找到我的.xml
文件之前我可以有幾個「級別」的 zip(請參閱下面的範例)。
我的要求是檢測哪一個根ZIP 檔案至少包含一個大於 100Mb 的 XML 檔案。當 ZIP 檔案處於這種情況時,應將其移至另一個目錄(例如說~/big-files
)。另外,如果非壓縮.xml
檔案大於 100Mb,則應將其移至此目錄。
例如:
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
因為它們至少託管一個大於 100Mb 的 XML 文件,但不是四.zip。
我怎麼能在 bash shell 中實現這一點?
謝謝。
答案1
首先,安裝AVFS,一個提供檔案內部透明存取的檔案系統,並運行命令mountavfs
。看如何遞歸地 grep 遍歷壓縮檔案?為背景。
此後,如果/path/to/archive.zip
是一個可識別的存檔,則~/.avfs/path/to/archive.zip#
是一個似乎包含該存檔內容的目錄。
編寫一個名為 的輔助腳本has_large_file_rec
,在作為參數傳遞的 zip 檔案中尋找大型 XML 文件,並在每個嵌入的 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
大於 100MB。每次解壓縮 zip 檔案時進行檢查會更快。它可以改進。 - 它不會快取多次處理的 zip 檔案。
~/big_files
必須存在並且可寫。- 該腳本不接受參數。您必須在
zip
和文件所在的目錄中運行它xml
。
正如您在前面的幾點中看到的那樣,它並不完美,但它在我的測試中有效。我希望它對你有用。
像這樣運行它:
perl script.pl