递归dircmp(比较两个目录以确保它们具有相同的文件和子目录)

Gre*_*ind 24 python recursion

从我观察到的filecmp.dircmp递归,但不满足我的需要,至少在py2.我想比较两个目录及其所有包含的文件.这是存在的,还是我需要构建(使用os.walk例如使用).我更喜欢预制,其他人已经完成了单元测试:)

实际的"比较"可以是草率的(例如,忽略权限),如果这有帮助的话.

我想要一些布尔值,并且report_full_closure是一份打印的报告.它也只是常见的子目录.AFIAC,如果左边或右边有任何东西,只有那些是不同的目标.我用它os.walk来构建它.

Mat*_*bos 24

这是比较功能与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
Run Code Online (Sandbox Code Playgroud)


Phi*_*nne 16

filecmp.dircmp是要走的路.但它没有比较两个比较目录中使用相同路径找到的文件的内容.而filecmp.dircmp只是查看文件属性.由于dircmp是一个类,您可以使用dircmp子类修复它,并覆盖其phase3比较文件的功能,以确保比较内容而不是仅比较os.stat属性.

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
Run Code Online (Sandbox Code Playgroud)

然后你可以用它来返回一个布尔值:

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
Run Code Online (Sandbox Code Playgroud)

如果您想重复使用此代码段,则特此专用于您选择的Public Domain或Creative Commons CC0(除了SO提供的默认许可证CC-BY-SA).


Gui*_*ent 6

这是一个带有递归函数的简单解决方案:

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'))
Run Code Online (Sandbox Code Playgroud)


ast*_*asr 5

report_full_closure()方法是递归的:

comparison = filecmp.dircmp('/directory1', '/directory2')
comparison.report_full_closure()
Run Code Online (Sandbox Code Playgroud)

编辑:OP编辑后,我会说最好只使用其他功能filecmp.我觉得没os.walk必要; 最好简单地通过由common_dirs等产生的列表进行递归,尽管在某些情况下(大型目录树),如果实施不当,这可能会导致Max Recursion Depth错误.


Kat*_*iel 3

dircmp可以是递归的:参见report_full_closure.

据我所知dircmp不提供目录比较功能。不过,自己编写也很容易;使用left_onlyand right_onlyondircmp检查目录中的文件是否相同,然后对subdirs属性进行递归。