2009-06-08 4 views
1

私は2つのディスクを持っています.1つはアドホックなバックアップディスクで、重複するものはどこにでもあり、別のディスクは私のラップトップにあります。私は一意のファイルをバックアップし、重複を削除する必要があります。だから、私は次のことを実行する必要があります。Perlを使用して1つまたは複数の重複ファイルシステムを整理する

  • すべての非ゼロサイズのファイル
  • は、重複するファイル名を持つファイル
  • にマスターとは別に独自のファイルを検索し、すべてのファイル
  • のMD5ダイジェストを計算して下さいその他のコピー。このスクリプトの出力と

私はします:

  • バックアップユニークなマスターファイル

ユニークなファイルが無い他のコピー

を=他のコピーを削除します。

マスターコピー =最初に他のコピーはおそらく

他のコピーが =コピー私は私には意味をなさないように思われる追加のスクリプトを、作成した

をマスターしていない優先パスに一致する、存在する場合、しかし:

総ファイル=ユニークなファイル+マスターコピー+他のコピー

私は2つの質問があります!

  1. WHEを私の論理に誤りがありますか?
  2. これを行うより効率的な方法はありますか?

巨大なファイルリストを処理する際に、ディスクハッシュを選択したため、メモリが不足していませんでした。

#!/usr/bin/perl 

use strict; 
use warnings; 
use DB_File; 
use File::Spec; 
use Digest::MD5; 

my $path_pref = '/usr/local/bin'; 
my $base = '/var/backup/test'; 

my $find = "$base/find.txt"; 
my $files = "$base/files.txt"; 

my $db_duplicate_file = "$base/duplicate.db"; 
my $db_duplicate_count_file = "$base/duplicate_count.db"; 
my $db_unique_file = "$base/unique.db"; 
my $db_master_copy_file = "$base/master_copy.db"; 
my $db_other_copy_file = "$base/other_copy.db"; 

open (FIND, "< $find"); 
open (FILES, "> $files"); 

print "Extracting non-zero files from:\n\t$find\n"; 
my $total_files = 0; 
while (my $path = <FIND>) { 
    chomp($path); 
    next if ($path =~ /^\s*$/); 
    if (-f $path && -s $path) { 
    print FILES "$path\n"; 
    $total_files++; 
    printf "\r$total_files"; 
    } 
} 

close(FIND); 
close(FILES); 
open (FILES, "< $files"); 

sub compare { 
    my ($key1, $key2) = @_; 
    $key1 cmp $key2; 
} 

$DB_BTREE->{'compare'} = \&compare; 

my %duplicate_count =(); 

tie %duplicate_count, "DB_File", $db_duplicate_count_file, O_RDWR|O_CREAT, 0666, $DB_BTREE 
    or die "Cannot open $db_duplicate_count_file: $!\n"; 

my %unique =(); 

tie %unique, "DB_File", $db_unique_file, O_RDWR|O_CREAT, 0666, $DB_BTREE 
    or die "Cannot open $db_unique_file: $!\n"; 

my %master_copy =(); 

tie %master_copy, "DB_File", $db_master_copy_file, O_RDWR|O_CREAT, 0666, $DB_BTREE 
    or die "Cannot open $db_master_copy_file: $!\n"; 

my %other_copy =(); 

tie %other_copy, "DB_File", $db_other_copy_file, O_RDWR|O_CREAT, 0666, $DB_BTREE 
    or die "Cannot open $db_other_copy_file: $!\n"; 

print "\nFinding duplicate filenames and calculating their MD5 digests\n"; 

my $file_counter = 0; 
my $percent_complete = 0; 

while (my $path = <FILES>) { 

    $file_counter++; 

    # remove trailing whitespace 
    chomp($path); 

    # extract filename from path 
    my ($vol,$dir,$filename) = File::Spec->splitpath($path); 

    # calculate the file's MD5 digest 
    open(FILE, $path) or die "Can't open $path: $!"; 
    binmode(FILE); 
    my $md5digest = Digest::MD5->new->addfile(*FILE)->hexdigest; 
    close(FILE); 

    # filename not stored as duplicate 
    if (!exists($duplicate_count{$filename})) { 
    # assume unique 
    $unique{$md5digest} = $path; 
    # which implies 0 duplicates 
    $duplicate_count{$filename} = 0; 
    } 
    # filename already found 
    else { 
    # delete unique record 
    delete($unique{$md5digest}); 
    # second duplicate 
    if ($duplicate_count{$filename}) { 
     $duplicate_count{$filename}++; 
    } 
    # first duplicate 
    else { 
     $duplicate_count{$filename} = 1; 
    } 
    # the master copy is already assigned 
    if (exists($master_copy{$md5digest})) { 
     # the current path matches $path_pref, so becomes our new master copy 
     if ($path =~ qq|^$path_pref|) { 
     $master_copy{$md5digest} = $path; 
     } 
     else { 
     # this one is a secondary copy 
     $other_copy{$path} = $md5digest; 
     # store with path as key, as there are duplicate digests 
     } 
    } 
    # assume this is the master copy 
    else { 
     $master_copy{$md5digest} = $path; 
    } 
    } 
    $percent_complete = int(($file_counter/$total_files)*100); 
    printf("\rProgress: $percent_complete %%"); 
} 

close(FILES);  

# Write out data to text files for debugging 

open (UNIQUE, "> $base/unique.txt"); 
open (UNIQUE_MD5, "> $base/unique_md5.txt"); 

print "\n\nUnique files: ",scalar keys %unique,"\n"; 

foreach my $key (keys %unique) { 
    print UNIQUE "$key\t", $unique{$key}, "\n"; 
    print UNIQUE_MD5 "$key\n"; 
} 

close UNIQUE; 
close UNIQUE_MD5; 

open (MASTER, "> $base/master_copy.txt"); 
open (MASTER_MD5, "> $base/master_copy_md5.txt"); 

print "Master copies: ",scalar keys %master_copy,"\n"; 

foreach my $key (keys %master_copy) { 
    print MASTER "$key\t", $master_copy{$key}, "\n"; 
    print MASTER_MD5 "$key\n"; 
} 

close MASTER; 
close MASTER_MD5; 

open (OTHER, "> $base/other_copy.txt"); 
open (OTHER_MD5, "> $base/other_copy_md5.txt"); 

print "Other copies: ",scalar keys %other_copy,"\n"; 

foreach my $key (keys %other_copy) { 
    print OTHER $other_copy{$key}, "\t$key\n"; 
    print OTHER_MD5 "$other_copy{$key}\n"; 
} 

close OTHER; 
close OTHER_MD5; 

print "\n"; 

untie %duplicate_count; 
untie %unique; 
untie %master_copy; 
untie %other_copy; 

print "\n"; 

答えて

0

一つの明白な最適化では、同じサイズで、2つのファイルの衝突を持っている場合、特定のサイズ以下のファイルの最初の比較の基礎として、ファイルサイズ、およびコンピュータのみのMD5を使用するか、またはすることです。ディスク上のファイルが大きいほど、MD5計算のコストが高くなりますが、正確なサイズがシステム上の別のファイルと競合する可能性は低くなります。あなたはおそらく自分自身で多くの実行時間を節約することができます。

基礎データを変更せずに変更される可能性のある埋め込みメタデータを含む特定の種類のファイルについては、MD5が一致しない場合でも追加の重複を見つけることができます。私は、クラシファイアやプレーヤープログラムによって更新される可能性のあるメタデータタグを持っているが、それ以外の場合は同じオーディオビットを含んでいるMP3やその他の音楽ファイルについて語っている。

1

これは実際にはプログラムのより大きなロジックに対する応答ではありませんが、毎回openのエラーをチェックする必要があります(また、現時点では、より現代的な形のopenを字句ファイルハンドルと3つの引数):

open my $unique, '>', "$base/unique.txt" 
    or die "Can't open $base/unique.txt for writing: $!"; 

明示的に毎回お願いしたくない場合は、あなたもautodieモジュールをチェックアウトすることができます。

+0

IOを使ってもっと近代的になります::ファイル –

+0

私は味のものとして私を襲います。私は本当にファイルを開くためにOOを望んでいませんが、好みは異なります。 「現代的」とは、Perlが語彙ファイルハンドルをサポートしていることを意味していますので、barewordsは必要ありません。 – Telemachus

2

アルゴリズムを見ると、ファイルが漏れている理由がわかります。あなたは、ファイルのコピーが発生した最初の時間は、あなたはそれを「ユニーク」レーベル:にあった

# delete unique record 
delete($unique{$md5digest}); 

だから、どんなファイルパス:

if (!exists($duplicate_count{$filename})) { 
    # assume unique 
    $unique{$md5digest} = $path; 
    # which implies 0 duplicates 
    $duplicate_count{$filename} = 0; 
} 

次回は、あなたがパスを保存せずに、そのユニークなレコードを削除します$ unique {$ md5digest}、あなたはそれを失ってしまい、+他の+マスターには含まれません。私は、IO::Fileは本当にこのコードをクリーンアップするでしょう上のコメントで述べたように、また

if(my $original_path = delete $unique{$md5digest}) { 
    // Where should this one go? 
} 

あなたのような何かをする必要があります。

0

抽象的な性質のソリューションに関する関連データについては、ここを参照してください。

https://stackoverflow.com/questions/405628/what-is-the-best-method-to-remove-duplicate-image-files-from-your-computer

重要な注意、同じくらい我々は同じMD5を持つ2つのファイルが同じファイルと信じたいと思いとして、それは必ずしも真実ではありません。あなたのデータがあなたに何かを意味するならば、MD5があなたに同じファイルであると言う候補者のリストに分解したら、ビットごとに直線的に実行して、それらが実際に同じであることを確認する必要があります。

このように、サイズが1ビットのハッシュ関数(MD5)を仮定すると、可能な組み合わせは2つしかありません。

0 1 

あなたのハッシュ関数から2つのファイルが両方とも同じファイルであるとは思わない "1"が返された場合は、 2ビットのハッシュを考えると

は、あなたが同じファイルであることを前提としないのと同じ値を返すだけで4つの可能な組み合わせの、

00 01 10 11 

2ファイルがあります。

は、3ビットのハッシュを考えると、あなたは同じファイルであることを前提としないのと同じ値を返すだけで8可能な組み合わせ

000 001 010 011 
100 101 110 111 

2ファイルがあります。

このパターンは、奇妙な理由のために人々が方程式に「チャンス」を入れ始めるという点で、ますます増えていきます。128ビット(MD5)であっても、同じハッシュを共有している2つのファイルは、が実際には同じファイルであることを意味しません。唯一の方法はを知ってすべてのビットを比較することです。

異なるビットが見つかると直ちに読み込みを中止することができますが、同一であることを確認するためには、すべてのビットを読み込む必要があるため、読み終わるとマイナーな最適化が発生します。

関連する問題