5

我是一名摄影师,做了很多备份。多年来,我发现自己拥有很多硬盘。现在我买了一台 NAS 并使用 rsync 在一个 3TB raid 1 上复制了我所有的照片。根据我的脚本,这些文件中有大约 1TB 是重复的。这来自于在我的笔记本电脑上删除文件之前进行多次备份并且非常混乱。我确实在旧硬盘上备份了所有这些文件,但如果我的脚本把事情搞砸了,那就太痛苦了。您能否看看我的重复查找器脚本并告诉我您是否认为我可以运行它?我在一个测试文件夹上尝试过,看起来还可以,但我不想在 NAS 上搞砸。

该脚本在三个文件中包含三个步骤。在第一部分中,我找到所有图像和元数据文件,并将它们放入搁置数据库(datenbank)中,并以它们的大小作为键。

import os
import shelve

datenbank = shelve.open(os.path.join(os.path.dirname(__file__),"shelve_step1"), flag='c', protocol=None, writeback=False)

#path_to_search = os.path.join(os.path.dirname(__file__),"test")
path_to_search = "/volume1/backup_2tb_wd/"
file_exts = ["xmp", "jpg", "JPG", "XMP", "cr2", "CR2", "PNG", "png", "tiff", "TIFF"]
walker = os.walk(path_to_search)

counter = 0

for dirpath, dirnames, filenames in walker:
  if filenames:
    for filename in filenames:
      counter += 1
      print str(counter)
      for file_ext in file_exts:
        if file_ext in filename:
          filepath = os.path.join(dirpath, filename)
          filesize = str(os.path.getsize(filepath))
          if not filesize in datenbank:
            datenbank[filesize] = []
          tmp = datenbank[filesize]
          if filepath not in tmp:
            tmp.append(filepath)
            datenbank[filesize] = tmp

datenbank.sync()
print "done"
datenbank.close()

第二部分。现在我删除列表中只有一个文件的所有文件大小,并创建另一个搁置数据库,其中 md5 哈希作为键,文件列表作为值。

import os
import shelve
import hashlib

datenbank = shelve.open(os.path.join(os.path.dirname(__file__),"shelve_step1"), flag='c', protocol=None, writeback=False)

datenbank_step2 = shelve.open(os.path.join(os.path.dirname(__file__),"shelve_step2"), flag='c', protocol=None, writeback=False)

counter = 0
space = 0

def md5Checksum(filePath):
    with open(filePath, 'rb') as fh:
        m = hashlib.md5()
        while True:
            data = fh.read(8192)
            if not data:
                break
            m.update(data)
        return m.hexdigest()


for filesize in datenbank:
  filepaths = datenbank[filesize]
  filepath_count = len(filepaths)
  if filepath_count > 1:
    counter += filepath_count -1
    space += (filepath_count -1) * int(filesize)
    for filepath in filepaths:
      print counter
      checksum = md5Checksum(filepath)
      if checksum not in datenbank_step2:
        datenbank_step2[checksum] = []
      temp = datenbank_step2[checksum]
      if filepath not in temp:
        temp.append(filepath)
        datenbank_step2[checksum] = temp

print counter
print str(space)

datenbank_step2.sync()
datenbank_step2.close()
print "done"

最后是最危险的部分。对于 evrey md5 密钥,我检索文件列表并执行额外的 sha1。如果匹配,我会删除该列表中除第一个文件之外的每个文件,并创建一个硬链接来替换已删除的文件。

import os
import shelve
import hashlib

datenbank = shelve.open(os.path.join(os.path.dirname(__file__),"shelve_step2"), flag='c', protocol=None, writeback=False)

def sha1Checksum(filePath):
    with open(filePath, 'rb') as fh:
        m = hashlib.sha1()
        while True:
            data = fh.read(8192)
            if not data:
                break
            m.update(data)
        return m.hexdigest()

for hashvalue in datenbank:
  switch = True
  for path in datenbank[hashvalue]:
    if switch:
      original = path
      original_checksum = sha1Checksum(path)
      switch = False
    else:
      if sha1Checksum(path) == original_checksum:
        os.unlink(path)
        os.link(original, path)
        print "delete: ", path
print "done"

你怎么看?非常感谢。

*如果这很重要:它是 Synology 713+ 并且具有 ext3 或 ext4 文件系统。

4

4 回答 4

1

注意:如果您不喜欢 Python,那么现有的工具可以为您完成繁重的工作:

https://unix.stackexchange.com/questions/3037/is-there-an-easy-way-to-replace-duplicate-files-with-hardlinks

于 2015-01-21T14:30:41.560 回答
1

这看起来不错,经过一番消毒(使其与 python 3.4 一起工作)后,我在我的 NAS 上运行了它。虽然我在备份之间有未修改的文件的硬链接,但已移动的文件正在被复制。这为我恢复了丢失的磁盘空间。

一个小问题是已经是硬链接的文件被删除并重新链接。无论如何,这不会影响最终结果。

确实稍微改变了第三个文件(“3.py”):

if sha1Checksum(path) == original_checksum:
     tmp_filename = path + ".deleteme"
     os.rename(path, tmp_filename)
     os.link(original, path)
     os.unlink(tmp_filename)
     print("Deleted {} ".format(path))

这样可以确保在发生电源故障或其他类似错误的情况下,不会丢失任何文件,但会留下尾随的“删除”。恢复脚本应该很简单。

于 2014-08-14T14:31:48.840 回答
1

为什么不逐字节比较文件而不是第二个校验和?十亿分之一的校验和可能会意外匹配,但直接比较不应该失败。它不应该更慢,甚至可能更快。当有两个以上的文件并且您必须互相读取原始文件时,它可能会更慢。如果你真的想要,你可以通过一次比较所有文件的块来解决这个问题。

编辑:

我不认为它需要更多的代码,只是不同。循环体是这样的:

data1 = fh1.read(8192)
data2 = fh2.read(8192)
if data1 != data2: return False
于 2013-06-22T02:08:30.587 回答
0

如何创建硬链接。

在linux中你做

sudo ln sourcefile linkfile

有时这可能会失败(对我来说有时会失败)。此外,您的 python 脚本需要在 sudo 模式下运行。

所以我使用符号链接:

ln -s sourcefile linkfile

我可以用os.path.islink检查它们

你可以在 Python 中调用这样的命令:

os.system("ln -s sourcefile linkfile")

或者像这样使用subprocess

import subprocess
subprocess.call(["ln", "-s", sourcefile, linkfile], shell = True)

查看从命令行硬链接与软链接的执行情况

当它工作时,你能发布你的整个代码吗?我也想用。

于 2013-06-22T09:18:54.583 回答