python 比较2个目录的子目录、子文件不同

说明

比较 path1、path2目录,以path1为基准,比较path2目录 的 所有子目录、子文件是否相同

  • 比较子目录规则:path1 存在的目录,但 path2 不存在,将其返回
  • 比较子文件规则:
    • path1 存在的子文件,但 path2 不存在的,将其返回
    • 如果path1和path2都有该文件,那么就比较sha512值,不相同就返回;

例子

path1 目录下:

  • 有目录:a、b
  • 文件:1.txt、2.txt

path2 目录下:

  • 有目录:a、c
  • 文件:1.txt、3.txt

返回:

  • [b]
  • [2.txt]

提示

比较的是 相对目录,因为要比较的是两个不同目录,如果比较绝对路径,将全都不同

代码

import os,hashlib
def diff_2path(path1 ,path2,ignores:list):
    '''
    以path1为基准,比较path2目录 的 所有子目录、子文件是否相同
    :param path1: 源路径(全路径)
    :param path2: 目标路径(全路径)
    :param ignores 忽略的path1的子目录、子文件(注意是相对路径,适用于 path1 目录改动 或 改文件夹名)
    :return: 返回元组,第一个是 boolean,第二个元素是list,记录不同的目录,第三个元素是list,记录不同的文件
    将 path1 存在的目录,但 path2 不存在的,返回
    将 path1 存在的子文件,但 path2 不存在的,返回;如果path1和path2都有该文件,那么就比较sha512值,不相同就返回;
    例子:
        path1 目录下,有目录:a、b,文件:1.txt、2.txt
        path2 目录下,有目录:a、c,文件:1.txt、3.txt
        返回:
            [b]
            [2.txt]
    '''
    path1 = os.path.normpath(path1)
    path2 = os.path.normpath(path2)

    # 将path1的子目录、子文件list中
    files1 = []
    # 要比较的目录放到该list中
    dirs1 = []
    # 遍历出要管理目录下的所有子文件、子文件夹
    walk(path1 ,files1 ,dirs1,ignores)

    # 将path2的子目录、子文件list中
    files2 = []
    # 要比较的目录放到该list中
    dirs2 = []
    # 遍历出要管理目录下的所有子文件、子文件夹
    walk(path2, files2, dirs2,ignores)

    # 截取相对路径
    folder_name1 = os.path.basename(path1)
    files1_relpath = relpath_list(files1,folder_name1)
    dirs1_relpath = relpath_list(dirs1,folder_name1)


    folder_name2 = os.path.basename(path2)
    files2_relpath = relpath_list(files2,folder_name2)
    dirs2_relpath = relpath_list(dirs2,folder_name2)



    # 比较path1、path2的所有子目录-----------------------
    dirs_chng = []
    for item in dirs1_relpath:
        if item in ignores:
            continue
        if item in dirs2_relpath:
            pass
        else:
            dirs_chng.append(item)


    # 比较path1、path2的所有子文件,要比较 sha512
    files_chng = []
    for i, item1 in enumerate(files1_relpath):
        diff = True # True表示不同;False表示相同
        for j, item2 in enumerate(files2_relpath):
            if item1 == item2:
                sha512_item1 = sha512(files1[i])
                sha512_item2 = sha512(files2[j])
                if sha512_item1 != sha512_item2:
                    diff = True
                else :
                    diff = False

                break

        if diff:
            files_chng.append(item1)

    if len(dirs_chng) > 0 or len(files_chng) > 0:
        return True ,dirs_chng ,files_chng
    else:
        return False ,dirs_chng ,files_chng


def walk(path,files:list,dirs:list,ignores:list):
    '''
    遍历文件夹、文件(不包含版本库文件件)
    :param path:
    :param files: 保存文件
    :param dirs: 保存目录
    :param ignores 忽略的path1的子目录、子文件(注意是相对路径,适用于 path1 目录改动 或 改文件夹名)
    :return:
    '''
    path = os.path.normpath(path)

    # 将 ignore 截取相对路径
    current_folder = os.path.basename(path)

    for item in os.scandir(path):
        item_relpath = relpath(item.path, current_folder)
        if item_relpath in ignores:
            continue
        if item.is_dir():
            # print("目录:",item.path)
            dirs.append(item.path)
            walk(item.path,files,dirs,ignores)
        elif item.is_file():
            # print("文件:",item.path)
            files.append(item.path)

def relpath(path,start):
    '''
    从start开始计算相对路径(不包含start)
    替代 os.path.relpath(path[, start])
    :param path:
    :param start:
    :return:
    '''
    path = os.path.normpath(path)
    if start == "":
        return path
    # print(path)
    path = path.replace("\\","/")
    # print(path)
    new_parts = []
    parts = path.split("/")
    append_tag = False
    for item in parts:
        if item == start:
            append_tag = True
            continue
        if append_tag:
            new_parts.append(item)
    if len(new_parts) == 0:
        raise ValueError("["+path+"] 不包含 ["+start+"]")

    new_path = "/".join(new_parts)
    new_path = os.path.normpath(new_path)
    return new_path


def relpath_list(paths,start):
    '''
    从start开始计算相对路径(不包含start)
    替代 os.path.relpath(path[, start])
    :param paths: list类型,路径
    :param start:
    :return:
    '''
    ret = []
    for item in paths:
        rel = relpath(item,start)
        ret.append(rel)
    return ret

def sha512(path):
    # hashlib.sha512()  or hashlib.md5()
    algorithm = hashlib.sha512()
    with open(path, 'rb') as f:
        algorithm.update(f.read())

    return algorithm.hexdigest()

if __name__ == "__main__":
    path1 = r"C:\Users\mym\Desktop\重庆发票2"
    path2 = r"C:\Users\mym\Desktop\重庆发票2\.fvm版本库\backup\2\data"
    ignore = [r"C:\Users\mym\Desktop\重庆发票2\.fvm版本库"]
    res,dirs_diff,files_diff = diff_2path(path1,path2,ignore)
    print(res)
    print(dirs_diff)
    print(files_diff)

原文出处:/show_1IX7SZsT6B0j.html