• 文件与目录差异对比模块filecmp


       简介

          当我们进行代码审计或校验备份结果时,往往需要检查原始与目标目录的文件一致性,python的标准库已经自带了满足此需求的模块filecmp。filecmp可以实现文件、目录、遍历子目录的差异对比功能。比如报告中输出目标目录比原始多出的文件或子目录,即使文件同名也会判断是否为同一个文件(内容级对比)等,python2.3以上版本自带了filecmp模块,无需额外安装。

    模块常用方法说明

          filecmp 提供了三个操作方法,分别为cmp(单文件对比)、cmpfiles(多文件对比)、dircmp(目录对比)

    单文件对比,采用filecmp.cmp(f1,f2[,shallow])方法,比较文件名为f1和f2的文件,相同返回True,不相同返回False,shallow默认为True,意思是只根据os.stat()方法返回的文件基本信息进行对比,比如访问的时间、修改时间、状态改变时间等,会忽略文件内容的对比。当shallow为 False时,则os.stat()与文件内容同时进行校验。

    >>> import filecmp
    >>> filecmp.cmp("nginx.conf.v1","nginx.conf.v2")
    False
    >>> 
    

     多文件对比,采用filecmp.cmpfiles(dir1,dir2,common[,shallow])方法,对比dir1与dir2目录给定文件清单。该方法返回文件名的三个列表,分别为匹配、不匹配、错误。匹配为包含匹配的文件列表,不匹配反之,错误列表包含了目录不存在的文件、不具备读权限或其他原因导致的不能比较的文件清单。

    示例:dir1和dir2目录中指定文件清单对比

    两个目录文件的md5信息如下,其中f1,f2文件匹配,f3不匹配,f4,f5对应目录中不存在,无法比较

    jbdeAir:difflib比对模块 ljb$ md5 dir1/*
    MD5 (dir1/f1) = 81dc9bdb52d04dc20036dbd8313ed055
    MD5 (dir1/f2) = 989922700777691e1336309077acf468
    MD5 (dir1/f3) = 3b3e5a6cdf65514ff9de7253418b608a
    MD5 (dir1/f5) = 33e28153f08dcd28a4c4292ad4c866af
    ljbdeAir:difflib比对模块 ljb$ md5 dir2/*
    MD5 (dir2/f1) = 81dc9bdb52d04dc20036dbd8313ed055
    MD5 (dir2/f2) = 989922700777691e1336309077acf468
    MD5 (dir2/f3) = 4d1a60976189c13c3a8a71baf1bde5f7
    MD5 (dir2/f4) = 025ad219ece1125a8f5a0e74e32676cb
    

      代码演示

    import filecmp
    filecmp.cmpfiles("/difflib比对模块/dir1","/difflib比对模块/dir2",['f1','f2','f3','f4','f5'])
    (['f1', 'f2'], ['f3'], ['f4', 'f5'])  

     目录比对,通过dircmp(a,b[,ignore[,hide]])创建一个目录比较对象,其中a和b是参加比较的目录名。ignore代表文件名忽略列表,并默认为['RCS','CVS','tags'];hide代表隐藏的列表,默认为[os.curdir,os.pardir]。dircmp可以获得目录比较的详细信息,如只有在a目录中包含的文件、a与b都存在的子目录、匹配的文件等,同时支持递归。

    dircmp提供了三个输出报告的方法:

    report(),比较当前指定目录中的内容;

    report_partial_closure(),比较当前指定目录及第一级子目录中的内容;

    report_full_closure(),递归比较所有指定目录的内容。

    为了输出更详细的比较结果,dircpm类还提供了以下属性:

    left,左目录,如类定义中的a;

    right,右目录,如类定义中的b;

    left_list,左目录中的文件及目录列表;

    right_list,右目录中的文件及目录列表;

    common,两边共同存在的文件或目录;

    left_only,只在左目录中的文件或目录;

    right_only,只在右目录中的文件或目录;

    common_dirs,两边目录都存在的子目录;

    common_files,两边目录都存在的子文件;

    common_funny,两边目录都存在的子目录(不同目录类型或os.stat()记录的错误);

    same_files,匹配相同的文件;

    diff_files,不匹配的文件;

    funny_files,两边目录中都存在,但无法比较的文件;

    subdirs,将common_dirs目录名映射到新的dircmp对象,格式为字典类型。

    示例:对比dir1和dir2的目录差异

    通过调用dircmp()方法实现目录功能差异对比功能,同时输出目录对比对象所有属性信息。

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    import filecmp
    
    a="/home/test/filecmp/dir1" #定义左目录
    b="/home/test/filecmp/dir2" #定义右目录
    
    dirobj=filecmp.dircmp(a,b,['test.py']) #目录比较,忽略test.py文件
    #输出对比结果数据报表,详细说明请参考filecmp类方法及属性信息
    
    print "-------------------report---------------------"
    dirobj.report()
    print "-------------report_partial_closure-----------"
    dirobj.report_partial_closure()
    print "-------------report_full_closure--------------"
    dirobj.report_full_closure()
    
    print "left_list:"+ str(dirobj.left_list)
    print "right_list:"+ str(dirobj.right_list)
    print "common:"+ str(dirobj.common)
    print "left_only:"+ str(dirobj.left_only)
    print "right_only:"+ str(dirobj.right_only)
    print "common_dirs:"+ str(dirobj.common_dirs)
    print "common_files:"+ str(dirobj.common_files)
    print "common_funny:"+ str(dirobj.common_funny)
    print "same_file:"+ str(dirobj.same_files)
    print "diff_files:"+ str(dirobj.diff_files)
    print "funny_files:"+ str(dirobj.funny_files)  

      文件目录树结构

    实践:校验源与备份目录差异

          有时候我们无法确认备份目录与源目录文件是否保持一致,包含源目录中的新文件或目录、更新文件或目录有无成功同步,定期进行校验,没有成功则希望有针对性的进行补备份。本示例使用了filecmp的left_only、diff_files方法递归获取源目录的更新项,再通过shutil.copyfile、os.makedirs方法对更新项进行复制,最终保持一致状态

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    
    import os, sys
    import filecmp
    import re
    import shutil
    
    holderlist = []
    
    
    def compareme(dir1, dir2):                  #递归获取更新函数
        dircomp = filecmp.dircmp(dir1, dir2)
        only_in_one = dircomp.left_only         #源目录新文件或目录
        diff_in_one = dircomp.diff_files        #不匹配文件,源目录文件已经发生变化
        dirpath = os.path.abspath(dir1)         #定义源目录的绝对路径
        #将更新文件名或目录追加到holderlist列表中
        [holderlist.append(os.path.abspath(os.path.join(dir1, x))) for x in only_in_one]
        [holderlist.append(os.path.abspath(os.path.join(dir1, x))) for x in diff_in_one]
        if len(dircomp.common_dirs) > 0:        #判断是否存在相同的子目录,以便递归
            for item in dircomp.common_dirs:    #递归子目录
                compareme(os.path.abspath(os.path.join(dir1, item)),
                          os.path.abspath(os.path.join(dir2, item)))
            return holderlist
    
    
    def main():
        if len(sys.argv) > 2:       #要求输入源目录和备份目录
            dir1 = sys.argv[1]
            dir2 = sys.argv[2]
        else:
            print "Usage: ", sys.argv[0], "datadir backupdir"
            sys.exit()
    
        source_files = compareme(dir1, dir2)    #对比源目录与备份目录
        dir1 = os.path.abspath(dir1)
    
        if not dir2.endswith('/'): dir2 = dir2 + '/'       #备份目录路径加 / 符
        dir2 = os.path.abspath(dir2)
        destination_files = []
        createdir_bool = False
    
        for item in source_files:       #遍历返回的差异文件,或目录清单
            destination_dir = re.sub(dir1, dir2, item)      #将源目录差异路径清单对应替换成备份目录
            destination_files.append(destination_dir)
            if os.path.isdir(item):                         #如果差异路径为目录且不存在,则再目录中创建
                if not os.path.exists(destination_dir):
                    os.makedirs(destination_dir)
                    createdir_bool = True           #再次调用compareme函数标记
    
        if createdir_bool:              #重新调用compareme函数,重新遍历新创建目录的内容
            destination_files = []
            source_files = []
            source_files = compareme(dir1, dir2)  #调用compareme函数
            for item in source_files:           #获取源目录差异路径清单,对应替换成备份目录
                destination_dir = re.sub(dir1, dir2, item)
                destination_files.append(destination_dir)
    
        print "update item:"
        print source_files      #输出更新项列表清单
    
        copy_pair = zip(source_files, destination_files)    #将源目录与备份目录文件清单拆分成元组
        for item in copy_pair:
            if os.path.isfile(item[0]):             #判断是否为文件,是则进行复制操作
                shutil.copyfile(item[0], item[1])
    
    
    if __name__ == '__main__':
        main()
    

      更新源目录dir1中f4、code/f3文件后,运行程序结果

    python dircmp_sample2.py  dir1 dir2

    update item:

    ['/dir1/f4','/dir1/code/f3']

    update item:

    [] #再次运行已经没有更新项目了

  • 相关阅读:
    QTP 参数化
    功能自动化测试流程
    Oracle客户端安装及配置
    描述性编程与对象库编程的对比
    Java用Scanner类获取用户输入
    Java入门的程序汇总
    Java入门学习知识点汇总
    Java最常用的变量定义汇总
    eclipse对Java程序的移植
    JavaScript关闭窗口的同时打开新页面的方法
  • 原文地址:https://www.cnblogs.com/Jabe/p/8970001.html
Copyright © 2020-2023  润新知