PHP, Ruby 에 이어서, Python 에서도 중복파일 찾기를 만들어보았다.

# -*- coding: cp949 -*-
# 버전 3.2.2 이상
 
from operator import itemgetter
from hashlib import md5
import os
 
TARGET_DIR   = "M:\\PATH\\TO\\특정디렉토리"
LIMITED_SIZE = 10*(1024*1024) # 10MB
 
 
def md5sum(filename, buf_size=4068):
    m = md5()
    with open(filename, 'rb') as f:
        data = f.read(buf_size)
        while data:
            m.update(data)
            data = f.read(buf_size)
    return m.hexdigest()
 
 
def main():
    hash_cnt  = {}
    file_list = []
    for p, ds, fs in os.walk(TARGET_DIR):
        for f in fs:
            filename = os.path.join(p, f)
            if not os.path.isfile(filename) : continue
            if os.path.islink(filename) : continue
            if os.path.getsize(filename) < LIMITED_SIZE: continue
            crc = md5sum(filename)
            if crc in hash_cnt:
                hash_cnt[crc] = hash_cnt[crc] + 1
            else:
                hash_cnt[crc] = 1
            file_list.append(crc+"|"+filename)
 
    for hash, cnt in sorted(hash_cnt.items(), key=itemgetter(1), reverse=True):
        if cnt < 2: continue
        print("\n["+hash+"]")
        for item in file_list:
            (hash2, filename) = item.split("|")
            if hash == hash2 : print(filename)
 
 
if __name__ == '__main__':
    main()

아래 코드에서 파일에 대한 MD5SUM 을 구하기 위해서 주석 처리한 부분을 대신 사용할 수 도 있다. 인터넷에서 찾아보면 이렇게 되어 있는 경우가 많은데, 이렇게 하면 메모리를 많이 잡아먹는 문제가 발생한다. 반드시 Digest::MD5.file().hexdigest 를 이용해야만 적은 메모리를 사용하면서 원활하게 동작되니 주의하기 바란다.

# -*- coding: cp949 -*-
require 'find'
require 'digest/md5'
 
file_list = Array.new
Find.find("C:\\") do |path|
  if File.file?(path) and File.size(path) > 10_000_000
    # digest = Digest::MD5.hexdigest(File.read(path))
    # --> 위와 같이 해도 되지만, 메모리를 많이 잡아먹는다.
    digest = Digest::MD5.file(path).hexdigest
    file_list << digest+"|"+path
  end
end
 
distinct_list = Hash.new(0)
file_list.each do |val|
  (md5, file) = val.split(/\|/)
  distinct_list[md5] += 1
end
 
distinct_list.each do |md5, count|
  if count >= 2
    print "\n["+md5+"]\n"
    file_list.each do |val|
      (md5_2, file) = val.split(/\|/)
      if md5 == md5_2
        print file+"\n"
      end
    end
  end
end

PHP가 처음에는 웹프로그래밍을 위해서 만들어졌지만, 지금은 CLI 를 지원함으로써, 일반 General Language 로써의 면모도 갖추고 있다. 그러므로, 시스템 관리/일반 어플리케이션 개발 등에도 널리 활용할 수 있다. 필자는 서버 관리를 주로 하고 있으므로, 시스템 관리를 위한 스크립트를 가끔 작성해야 하는데, 이번에는 PHP로 해보았다. 파일서버 관리를 위해서 주기적으로 오래되거나 필요없는 파일들을 삭제해주어야 하는데, 이 중에서도 중복되는 파일을 제거해주는 것이 꽤 효과적이다. 내가 지울수 있는 권한은 없으니, 중복파일 목록을 사용자들에게 주고 직접 지우도록 하면 그나마 쬐끔 용량을 확보하는데 도움이 된다.

아래는 10MB 넘는 파일중에서 중복된 파일을 찾아서 파일에 결과물을 남기도록 하였다. 기존에 Perl, Python, Ruby 등으로 만들어 놓긴 했지만, PHP로 만들어보니 꽤 훌륭하게 동작한다. ^^ 코드는 그다지 최적화된 것 같지는 않지만, 참고는 할 만 할 것이다.

// -----------------------------------------
// 10MB 이상의 파일중에서 중복된 파일 찾기
// -----------------------------------------
$limit_size  = 10000000; // 10MB
$dst_dir     = "M:\\";
$file_list   = array();
$result_list = array();
// -----------------------------------------
 
$start_time = date("Y/m/d H:i:s", time());
 
$i = 0;
$rdi = new RecursiveDirectoryIterator($dst_dir);
try {
    foreach (new RecursiveIteratorIterator($rdi, 
                                            RecursiveIteratorIterator::LEAVES_ONLY, 
                                            RecursiveIteratorIterator::CATCH_GET_CHILD) as $path) {
        if ($path->isFile() && $path->getSize() > $limit_size) {
            $md5sum = md5_file($path->__toString());
            echo "($i) [$md5sum] : ".$path->__toString()."\n";
            $file_list[$i++] = $md5sum."\t".$path->__toString();
        }
 
    }
} catch(Exception $e) {
    echo "Message: ".$e->getMessage();
}
 
 
foreach($file_list as $key => $value) {
    list($md5, $path) = split("\t", $value, 2);
    if(!isset($result_list[$md5])) { $result_list[$md5] = 0; }
    $result_list[$md5] += 1;
}
 
 
$i = 0;
$fh = fopen("result_samefiles_".date("Ymd").".txt", "w") or die("Can't open file");
foreach($result_list as $key => $value) {
    if($value < 2) { continue; }
 
    // fputs ($fh, "\n[$key]\n");
    fputs ($fh, "\n[$i]\n");
    ++$i;
    foreach($file_list as $idx => $file) {
        list($md5, $path) = split("\t", $file, 2);
        if($md5 == $key) {
            fputs ($fh, "$path\n");
        }
    }
}
fclose($fh);
 
 
$end_time = date("Y/m/d H:i:s", time());
 
echo "\nStart Time -> $start_time \n";
echo "End Time   -> $end_time \n\n";

+ Recent posts