重複排除スクリプト 1号
概要
ファイル数がめちゃくちゃ多い場合にも対応する
よりシンプルな重複排除スクリプトについて。
動作概要
- 渡されたファイルをキャッシュから探し、無ければ内容を読む。
- 内容が同じファイルがあったら「ハードリンク」する
- 1 にもどる
hashmarge.py
#!/usr/bin/python # -*- coding: utf-8 -*- import sqlite3,sys,os,pickle,hashlib,base64 class HmDBO: con = None def __init__(self,dbpath): self.con = sqlite3.connect(dbpath) if not self.existFileTable(): self.createFileTable() if not self.existCacheTable(): self.createCacheTable() def commit(self): self.con.commit() def close(self): self.con.close() self.con = None # #filehash # def existFileTable(self): sql = u"""SELECT * FROM sqlite_master WHERE type='table' AND name='filehash';""" c = self.con.execute(sql) for o in c: return True return False def createFileTable(self): sql = u"""create table filehash(path varchar(64) primary key, hash varchar(64));""" self.con.execute(sql) def insertFile(self,path,hash): sql = u"""insert into filehash values (?,?); """ hxpath = hashlib.sha256(path).hexdigest() self.con.execute(sql,(hxpath,hash)) def selectFile(self,path): sql = u"""select hash from filehash where path=?;""" hxpath = hashlib.sha256(path).hexdigest() c = self.con.execute(sql,(hxpath,)) for o in c: return o[0] return None # #linkcache # def existCacheTable(self): sql = u"""SELECT * FROM sqlite_master WHERE type='table' AND name='linkcache';""" c = self.con.execute(sql) for o in c: return True return False def createCacheTable(self): sql = u"""create table linkcache(hash varchar(64) primary key, path text);""" self.con.execute(sql) def insertCache(self,hash,path): sql = u"""insert into linkcache values (?,?)""" b64path = base64.b64encode(path) self.con.execute(sql,(hash,b64path)) def selectCache(self,hash): sql = u"""select path from linkcache where hash=?;""" c = self.con.execute(sql,(hash,)) for o in c: return base64.b64decode(o[0]) return None def deleteCache(self,hash): sql = u"""delete from linkcache where hash=?;""" self.con.execute(sql,(hash,)) def calcSHA256(path): sha = hashlib.sha256() fp = open(path,'r') while True: cache = fp.read(65536) if not cache: break sha.update(cache) fp.close() return sha.hexdigest() def hashmarge(dbpath): filecount=0 margecount=0 registercount=0 dbo = HmDBO(dbpath) for rwln in iter(sys.stdin.readline,""): path = rwln.rstrip('\n') if os.path.isfile(path): sys.stderr.write(path + '...') filecount += 1 #filehash hash = dbo.selectFile(path) if hash == None: registercount += 1 sys.stderr.write('register ') hash = calcSHA256(path) dbo.insertFile(path,hash) #linkcache cache = dbo.selectCache(hash) if cache == None: dbo.insertCache(hash,path) elif os.path.isfile(cache) and not os.path.samefile(cache, path): margecount += 1 sys.stderr.write('marge ') os.remove(path) os.link(cache, path) sys.stderr.write('done\n') dbo.commit() dbo.close() sys.stderr.write('files = ' + str(filecount) + '\n') sys.stderr.write('register = ' + str(registercount) + '\n') sys.stderr.write('marges = ' + str(margecount) + '\n') def rebuild(olddb,newdb): old = HmDBO(olddb) new = HmDBO(newdb) for rwln in iter(sys.stdin.readline,""): sys.stderr.write(rwln) path = rwln.rstrip('\n') if os.path.isfile(path): hash = old.selectFile(path) if hash <> None: new.insertFile(path,hash) cache = new.selectCache(hash) if cache == None: new.insertCache(hash, path) new.commit() old.close() new.close() def main(): if len(sys.argv) < 2: exit() elif len(sys.argv) == 2: hashmarge(sys.argv[1]) elif len(sys.argv) == 3: rebuild(sys.argv[1], sys.argv[2]) if __name__ == '__main__': main()
使い方
前回と一緒
$ find <対象ディレクトリ> | hashmarge.py <dbファイル>
あとがき
前回のエントリで公開したスクリプトと比較して
こちらはファイル数が異常に多くても問題なく動作することが強み
シンプルなのでおそらくwindows移植も可能かも?
samefile()が未対応のため windowsでは動作しませんでした
なんとかしてみる
windowsでsamefile()する方法をググったら出てきた
Tim Golden's Python Stuff: See if two files are the same file
なるほど。
これでwindows対応版を作ればよさそうだ