递归比较两个目录以确保文件和子目录相同

46 投票
15 回答
79548 浏览
提问于 2025-04-16 07:06

根据我的观察,filecmp.dircmp 是一种可以递归比较的工具,但对我来说不太够用,至少在Python 2中是这样。我想比较两个文件夹及其里面所有的文件。请问有没有现成的工具可以用,还是说我需要自己动手写一个(比如用os.walk)?我更喜欢那些已经有人做过单元测试的现成工具,这样省事多了 :)

其实“比较”的过程可以简单点(比如可以忽略文件权限),如果这样能帮到你。

我想要的结果是一个布尔值,而report_full_closure 是一个打印出来的报告。它只会比较共同的子目录。对我来说,如果左边或右边的文件夹里有东西,只有那些就是不同的文件夹。我是用os.walk来实现这个功能的。

15 个回答

9

这里有一个简单的解决方案,使用了递归函数:

import filecmp

def same_folders(dcmp):
    if dcmp.diff_files or dcmp.left_only or dcmp.right_only:
        return False
    for sub_dcmp in dcmp.subdirs.values():
        if not same_folders(sub_dcmp):
            return False
    return True

same_folders(filecmp.dircmp('/tmp/archive1', '/tmp/archive2'))
27

filecmp.dircmp 是一个很好的工具,但它只比较两个目录中相同路径的文件的属性,而不比较文件的内容。也就是说,filecmp.dircmp 只关注文件的基本信息,比如大小、修改时间等。因为 dircmp 是一个类,所以你可以通过创建一个 dircmp 的子类,并重写它的 phase3 函数,来实现文件内容的比较,而不仅仅是比较文件的属性。

import filecmp

class dircmp(filecmp.dircmp):
    """
    Compare the content of dir1 and dir2. In contrast with filecmp.dircmp, this
    subclass compares the content of files with the same path.
    """
    def phase3(self):
        """
        Find out differences between common files.
        Ensure we are using content comparison with shallow=False.
        """
        fcomp = filecmp.cmpfiles(self.left, self.right, self.common_files,
                                 shallow=False)
        self.same_files, self.diff_files, self.funny_files = fcomp

这样你就可以用它来返回一个布尔值(真或假):

import os.path

def is_same(dir1, dir2):
    """
    Compare two directory trees content.
    Return False if they differ, True is they are the same.
    """
    compared = dircmp(dir1, dir2)
    if (compared.left_only or compared.right_only or compared.diff_files 
        or compared.funny_files):
        return False
    for subdir in compared.common_dirs:
        if not is_same(os.path.join(dir1, subdir), os.path.join(dir2, subdir)):
            return False
    return True

如果你想重复使用这个代码片段,它可以被视为公共领域的作品,或者根据你的选择使用创意共享 CC0 许可(除了 SO 默认提供的 CC-BY-SA 许可)。

37

这里有一个使用 filecmp 模块的比较函数的替代实现。它采用了递归的方法,而不是使用 os.walk,所以会简单一些。不过,它并不是单纯通过使用 common_dirssubdirs 属性来递归,因为那样的话,我们就会隐含地使用默认的“浅层”文件比较方法,这可能不是你想要的。在下面的实现中,当比较同名文件时,我们始终只比较它们的内容。

import filecmp
import os.path

def are_dir_trees_equal(dir1, dir2):
    """
    Compare two directories recursively. Files in each directory are
    assumed to be equal if their names and contents are equal.

    @param dir1: First directory path
    @param dir2: Second directory path

    @return: True if the directory trees are the same and 
        there were no errors while accessing the directories or files, 
        False otherwise.
   """

    dirs_cmp = filecmp.dircmp(dir1, dir2)
    if len(dirs_cmp.left_only)>0 or len(dirs_cmp.right_only)>0 or \
        len(dirs_cmp.funny_files)>0:
        return False
    (_, mismatch, errors) =  filecmp.cmpfiles(
        dir1, dir2, dirs_cmp.common_files, shallow=False)
    if len(mismatch)>0 or len(errors)>0:
        return False
    for common_dir in dirs_cmp.common_dirs:
        new_dir1 = os.path.join(dir1, common_dir)
        new_dir2 = os.path.join(dir2, common_dir)
        if not are_dir_trees_equal(new_dir1, new_dir2):
            return False
    return True

撰写回答