确定目录中是否有文件添加、删除或修改

7 投票
4 回答
2883 浏览
提问于 2025-04-17 01:36

我正在尝试写一个Python脚本,用来获取一个目录下所有文件的md5校验和(在Linux系统上)。我觉得下面的代码已经实现了这个功能。

我希望能运行这个脚本,以确保目录中的文件没有被修改,也没有文件被添加或删除。

问题是,如果我对目录中的一个文件进行了修改,然后又把它改回去,运行下面的函数时却得到不同的结果。(即使我把修改过的文件又改回来了。)

有没有人能解释一下这个情况?如果你有其他解决办法,也请告诉我。

def get_dir_md5(dir_path):
    """Build a tar file of the directory and return its md5 sum"""
    temp_tar_path = 'tests.tar'
    t = tarfile.TarFile(temp_tar_path,mode='w')  
    t.add(dir_path)
    t.close()

    m = hashlib.md5()
    m.update(open(temp_tar_path,'rb').read())
    ret_str = m.hexdigest()

    #delete tar file
    os.remove(temp_tar_path)
    return ret_str

补充:正如这些朋友们所说,tar格式似乎包含了像修改日期这样的头信息。那使用zip格式会有什么不同吗,或者其他格式呢?

还有其他解决办法的想法吗?

4 个回答

3

你并不需要制作TAR文件来实现你想做的事情。

这里有一个替代的方法:

  1. 遍历整个文件夹结构;
  2. 获取每个文件的md5签名;
  3. 对这些签名进行排序;
  4. 将所有文件的签名合成一个文本字符串,然后再计算这个字符串的md5签名。

最后得到的这个签名就是你想要的结果。

其实你甚至不需要用Python来做这个,你可以这样做:

find /path/to/dir/ -type f -name *.py -exec md5sum {} + | awk '{print $1}'\
| sort | md5sum
7

TAR文件的头部信息里有一个字段是用来记录文件的修改时间的。也就是说,只要你对文件进行了一次修改,即使后来又把它改回原来的样子,这个TAR文件的头部信息就会发生变化,导致它的哈希值也会不同。

8

正如其他回答提到的,即使两个tar文件的内容完全一样,它们也可能会不同。这是因为tar文件的元数据(一些文件的附加信息)可能发生了变化,或者文件的顺序不同。为了比较这两个文件,你应该直接对文件的数据进行校验和计算,同时要把目录列表排序,确保它们的顺序始终一致。如果你想把一些元数据也算在内,可以手动添加这些信息。

下面是一个使用 os.walk 的未测试示例:

import os
import os.path
def get_dir_md5(dir_root):
    """Build a tar file of the directory and return its md5 sum"""

    hash = hashlib.md5()
    for dirpath, dirnames, filenames in os.walk(dir_root, topdown=True):

        dirnames.sort(key=os.path.normcase)
        filenames.sort(key=os.path.normcase)

        for filename in filenames:
            filepath = os.path.join(dirpath, filename)

            # If some metadata is required, add it to the checksum

            # 1) filename (good idea)
            # hash.update(os.path.normcase(os.path.relpath(filepath, dir_root))

            # 2) mtime (possibly a bad idea)
            # st = os.stat(filepath)
            # hash.update(struct.pack('d', st.st_mtime))

            # 3) size (good idea perhaps)
            # hash.update(bytes(st.st_size))

            f = open(filepath, 'rb')
            for chunk in iter(lambda: f.read(65536), b''):
                hash.update(chunk)

    return hash.hexdigest()

撰写回答