我需要一种方法来比较两个不同的文件套位置,最好能比文件名更详细一点。
我们最近为办公室买了一台新 NAS,并将数据从各种 USB 硬盘移到该 NAS 上。我希望能够确认所有文件都已成功移到该 NAS 上。
我查看过许多文件比较程序,但大多数程序对文件所在的目录结构都很敏感。理想情况下,我只想对驱动器 USB1、USB2 和 USB3 上的所有文件进行哈希处理(MD5 或类似处理),然后对 NAS-VOLUME1 和 NAS-VOLUME2 上的所有文件进行哈希处理,并比较列表以查看哪一侧缺少哪些文件。
我怀疑这可以通过脚本或命令行来完成,但我对在 OSX 中使用命令行不太熟悉(通常是 Windows 类型的人)。
任何指示都非常感谢
答案1
fdupes
如果 Mac 上有的话,可以这样做。您可以挂载所有磁盘并让 fdupes 在所有目录中运行。这样做也是最有效的(只比较大小相同的文件,因为具有唯一文件大小的文件不可能是重复的,等等)。要小心,因为 fdupes 通常用于删除不需要的重复项,因此很多示例可能包含删除选项。
答案2
如果两个位置的文件名和子目录结构相同,则此版本有效。此版本的优点是它应该是内存友好的。这已经针对简单错误进行了简单的测试,但可能还有更多需要考虑的问题。此外,由于 python 的开销很大,因此 bash 方法会更加高效。根据数据的大小,这可能需要很长时间。
import os
import hashlib
def hash(file):
f = open(file,'rb')
h = hashlib.md5()
checkEOF = b' '
while checkEOF != b'':
checkEOF = f.read(1024)
h.update(checkEOF)
f.close()
return h.hexdigest()
def Hashwalk(d1, d2):
errlist = []
log = []
walkobject1 = os.walk(d1)
walkobject2 = os.walk(d2)
try:
for walks in zip(walkobject1,walkobject2):
dir1 = walks[0][0]
dir2 = walks[1][0]
files1 = walks[0][2]
files2 = walks[1][2]
for files in zip(files1,files2):
try:
pathfile1 = os.path.join(dir1,files[0])
pathfile2 = os.path.join(dir2,files[1])
digest1 = hash(pathfile1)
digest2 = hash(pathfile2)
if digest1 != digest2:
log.append((pathfile1, digest1, pathfile2, digest2))
except PermissionError as error:
errlist.append((pathfile1,error))
except FileNotFoundError as error:
errlist.append((pathfile1,error))
except KeyboardInterrupt:
print('Program terminated, results may be incomplete')
return (log,errlist)
def ToDisk(hw):
diff = open('differenthashes.txt','w',encoding='utf-8')
for pair in hw[0]:
diff.write(pair[0]+','+pair[1]+'\n')
diff.write(pair[2]+','+pair[3]+'\n')
if hw[1]:
diff.write('\nerrors\n')
for error in hw[1]:
diff.write(error[0]+','+error[1]+'\n')
else:
diff.write('no errors detected')
diff.close()
ToDisk(Hashwalk('test1','test2'))
答案3
目前,此脚本唯一会考虑的错误是 PermissionError 和 FileNotFoundError。某些字符无法正确处理,因为它们使用其编码字符串表示,这导致了 FileNotFoundError。我添加了一个 KeyboardInterrupt 异常,以防脚本运行时间过长并且您想要查看累积结果。运行此脚本的目录将包含一个名为 differenthashes.txt 的文件。
要执行,只需在底部的 compare() 调用中替换“path1”和“path2”。如果您有任何建议或认为这不能满足您的需求,请告诉我。
import os
import hashlib
import time
def hash(file):
f = open(file,'rb')
h = hashlib.md5()
checkEOF = b' '
while checkEOF != b'':
checkEOF = f.read(1024)
h.update(checkEOF)
f.close()
return h.hexdigest()
def hashwalk(d = './'):
errlist = []
hashes = []
cwd = os.getcwd()
os.chdir(d)
walkobject = os.walk('./')
try:
for directory in walkobject:
dir = directory[0]
files = directory[2]
for file in files:
try:
pathfile = os.path.join(dir,file)
digest = hash(pathfile)
hashes.append((pathfile,digest))
except PermissionError as error:
errlist.append((pathfile,error))
except FileNotFoundError as error:
errlist.append((pathfile,error))
except KeyboardInterrupt:
print('Program terminated, results may be incomplete')
os.chdir(cwd)
return [hashes,errlist]
def compare(path1,path2,logerrors = False):
loc1 = hashwalk(path1)
loc2 = hashwalk(path2)
differenthash = set(loc1[0]).symmetric_difference(set(loc2[0]))
log = open('differenthashes.txt','w',encoding='utf-8')
log.write('path hash date modified\n')
for f,h in sorted(differenthash):
if (f,h) in loc1[0]:
print(path1+'\\'+f[2:],h,time.ctime(os.stat(path1+'\\'+f[2:]).st_mtime))
log.write(path1 + ' ' +f[2:] + ' ' + h + ' ' + time.ctime(os.stat(path1+'\\'+f[2:]).st_mtime)+'\n')
else:
print(path2+'\\'+f[2:],h,time.ctime(os.stat(path2+'\\'+f[2:]).st_mtime))
log.write(path2 + ' ' +f[2:] + ' ' + h + ' ' + time.ctime(os.stat(path2+'\\'+f[2:]).st_mtime)+'\n')
if logerrors:
log.write('\n\n'+path1+' errors\n')
for error in loc1[1]:
log.write(str(error) + '\n')
log.write('\n'+path2+' errors\n')
for error in loc2[1]:
log.write(str(error) +'\n')
log.close()
compare('path1', 'path2' ,logerrors=True)