Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何使用python用硬链接替换重复文件?_Python_Linux_Duplicates_Nas_Hardlink - Fatal编程技术网

如何使用python用硬链接替换重复文件?

如何使用python用硬链接替换重复文件?,python,linux,duplicates,nas,hardlink,Python,Linux,Duplicates,Nas,Hardlink,我是一名摄影师,做过很多备份。多年来,我发现自己有很多硬盘。现在我买了一个NAS,并使用rsync在一个3TB raid 1上复制了我所有的图片。根据我的脚本,这些文件中大约有1TB是重复的。这是因为我在删除笔记本电脑上的文件之前做了多次备份,而且非常混乱。我确实在旧硬盘上备份了所有这些文件,但如果我的脚本把事情搞砸了,那会很痛苦。你能看一下我的重复查找脚本并告诉我你认为我能不能运行它吗?我在一个测试文件夹上试过,看起来还可以,但我不想在NAS上搞砸 脚本在三个文件中包含三个步骤。在第一部分中,

我是一名摄影师,做过很多备份。多年来,我发现自己有很多硬盘。现在我买了一个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文件系统。

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

编辑:

我不认为它需要更多的代码,只是不同而已。对于循环体,类似于以下内容:

data1 = fh1.read(8192)
data2 = fh2.read(8192)
if data1 != data2: return False

如何创建硬链接

在linux中是这样的

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

所以我使用符号链接:

ln -s sourcefile linkfile
我可以用电脑查一下

您可以在Python中调用如下命令:

os.system("ln -s sourcefile linkfile")
或者使用以下类似方法:

看一看


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

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

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

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


这样可以确保在发生电源故障或其他类似错误时,不会丢失任何文件,尽管会留下一个尾随的“deleteme”。恢复脚本应该非常简单。

注意:如果您不热衷于Python,那么有一些现成的工具可以帮您完成繁重的工作:


不要立即删除,而是将副本移动到另一个文件夹,然后在您满意没有丢失任何内容时将其全部删除。不幸的是,3TB NAS已满。我只剩下20GB,所以我必须删除它。此外,我说的是139.020重复文件。我无法手动控制脚本是否出错。@JasonTS:将文件移动到同一文件系统上的另一个目录不会浪费任何空间,创建128K硬链接将浪费大约1兆字节的空间(可能比您的
搁置
数据库小),所以这可能不是拒绝怀疑论者建议的一个好理由。同时,我认为这个问题是关于,而不是堆栈溢出的。@abarnert:啊,对不起,我想到了一个副本。那可能很好。但是我很快就需要空间,所以我真的认为我没有足够的时间去看是否出了什么问题。谢谢你的提示。我也在CodeReview中发布了它。否则我觉得它还可以,但我不熟悉您使用的所有API。我正在做一个有根据的猜测,关于他们做什么。谢谢!我决定不使用软链接,因为我不知道文件应该放在哪里。我稍后会尝试手动清理。但现在我真的需要空间。通过硬链接,我将删除哪个“文件”并不重要。但是有了软链接,如果我想保留数据,我只能删除“真实文件”。另外,我认为我的一些照片编辑软件不喜欢软链接。但我认为你是对的,创建链接可能会失败,我应该为它失败的时候设置一个例外。我不需要使用sudo,因为我是以root身份运行的。除了上面的照片外,什么都没有。在这种情况下,软链接是危险的,因为删除一个备份会破坏包含同一文件的所有其他备份。您也不需要以root身份运行来创建硬链接。
import subprocess
subprocess.call(["ln", "-s", sourcefile, linkfile], shell = True)
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))