Groovy 에서도 100MB 넘는 파일 중에서 중복된 파일을 찾는 프로그램을 만들어보았다. 모든 언어를 공부하면서 만들어보는 것인데, MD5 Checksum 이 같으면 중복된 파일로 인식하고, 카운트하여 정렬하고 출력하도록 하였다. 파일에 대한 MD5 Checksum 기능이 없어서 직접 구현되었고, 이 부분을 제외하면 Ruby 와 거의 비슷하다.
// -----------------------------------------------------------------------------
// 100MB 이상의 파일중에서 중복된 파일 찾기
// -----------------------------------------------------------------------------
 
import java.security.MessageDigest
 
final TARGET_DIR = "C:\\"
final LIMIT_SIZE = 100000000
 
def md5sum(final file) {
    MessageDigest digest = MessageDigest.getInstance("MD5")
    file.withInputStream() { is ->          
        byte[] buffer = new byte[8192]
        int read = 0
        while( (read = is.read(buffer)) > 0) {
            digest.update(buffer, 0, read);
        }
    }                                                        
    byte[] md5 = digest.digest()
    BigInteger bigInt = new BigInteger(1, md5)
    return bigInt.toString(16).padLeft(32, '0')
}
 
def file_list     = []
def distinct_list = [:]
def startDate = new Date().format('yyyy/MM/dd HH:mm:ss')
 
new File(TARGET_DIR).eachFileRecurse { file -> 
    if( file.size() > LIMIT_SIZE ) {
        def md5 = null
        try {
            md5 = md5sum(file)
        } catch(ex) {
            md5 = null
        }
        if (md5 != null) { 
            if(distinct_list[md5] == null)
                distinct_list[md5] = 1
            else
                distinct_list[md5] += 1
            file_list << (md5+"|"+file)
        }
    }
}
 
distinct_list = distinct_list.sort { a, b -> b.value <=> a.value }
 
distinct_list.each { md5, cnt ->
    if( cnt > 1) {
       println "\n[ $md5 ]"
       file_list.each { file -> 
           def (md5_2, filename) = file.split(/\|/)
           if(md5 == md5_2) { println filename }
       }
    }
}
 
def endDate = new Date().format('yyyy/MM/dd HH:mm:ss')
println "\n\n\n"
println "================================================================================"
println "Start Time : $startDate"
println "End   Time : $endDate"
println "================================================================================"



Ruby에서 파일(디렉토리)이름에 특정 문자열이 포함된 목록 출력하기위해서 아래와 같이 하면 된다. Unix(Linux)/Cygwin 에서는 기본적인 도구만으로도 쉽게 할 수 있지만, 그런 환경에 안되는 곳에서는 유용하다. 아주 사소한 팁이지만, 이런 것이 여러가지가 모이면, 나중에 큰 도움이 될 것이다. ^^

# -*- coding: cp949 -*-
require 'find'
 
$dirlist    = ["C:\\"]
$sub_string = "애니메이션"
 
$dirlist.each() do |dirname|
  puts dirname if dirname.include?($sub_string)
 
  Find.find(dirname) do |file|
    next if file == nil
    next if not File.file?(file)
    next if not file.include?($sub_string)
 
    puts file
  end
end


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

+ Recent posts