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

摘要:
简介当我们进行代码审计或校验备份结果时,往往需要检查原始与目标目录的文件一致性,python的标准库已经自带了满足此需求的模块filecmp。filecmp可以实现文件、目录、遍历子目录的差异对比功能。比如报告中输出目标目录比原始多出的文件或子目录,即使文件同名也会判断是否为同一个文件等,python2.3以上版本自带了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第1张

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

有时候我们无法确认备份目录与源目录文件是否保持一致,包含源目录中的新文件或目录、更新文件或目录有无成功同步,定期进行校验,没有成功则希望有针对性的进行补备份。本示例使用了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文件后,运行程序结果

pythondircmp_sample2.py dir1 dir2

update item:

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

update item:

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

免责声明:文章转载自《文件与目录差异对比模块filecmp》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇【转】.Net 读取xml1、PyCharm的下载、安装下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

kubernetes配置(kubeconfig)对多集群的访问

配置对多集群的访问 本文展示如何使用配置文件来配置对多个集群的访问。 在将集群、用户和上下文定义在一个或多个配置文件中之后,用户可以使用kubectl config use-context命令快速地在集群之间进行切换。 注意:用于配置集群访问的文件有时被称为kubeconfig 文件。 这是一种引用配置文件的通用方式,并不意味着存在一个名为kubecon...

java利用SuffixFileFilter统计目录下特定后缀名文件的数目

/** * 文件处理类 * @author zhangcd * @date 2017年1月3日 */ public class FileUtil { /** * 得到所有后缀的数目 * * @param directory 目录 * @param suffixFilter 后缀 * @...

Ubuntu 下ftp服务器的安装配置

Ubuntu 下ftp服务器的安装配置 FTP服务器是平时应用最为广泛的服务之一。VSFTP是Very Secure FTP的缩写,意指非常安全的FTP服务。VSFTP功能强大,通过结合本地系统的用户认证模块及其多功能的配置项目,可以快速有效的搭建强大的多用户FTP服务。 首先我们考虑搭建FTP服务需要关注的都有哪些方面?比如我们最经常关注的有: 1、如...

Python—模块

Python—模块 一、模块模块,是用一堆代码实现了某个功能的代码集合,模块分为三种:自定义模块(自己定义)、内置模块(python自带)、开源模块导入模块(1)、导入一个py文件,解释器解释该py文件(2)、导入一个包,解释器解释该包下的 __init__.py 文件 #模块导入import modulefrom module.xx import xxf...

Dockerfile构建容器镜像

在Docker的运用中,从下载镜像,启动容器,在容器中输入命令来运行程序,这些命令都是手工一条条往里输入的,无法重复利用,而且效率很低。所以就需要一 种文件或脚本,我们把想执行的操作以命令的方式写入其中,然后让docker读取并分析、执行,那么重复构建、更新将变得很方便,所以Dockerfile就此诞生了。Docker提供了Dockerfile作为构建Do...

ubuntu基本命令

1、帮助      --help简单的帮助      help command 较详细的帮助      man command 最详细的帮助2、ls 命令      ls -a 显示全部的文件及文件夹,包括隐藏的文件或文件夹      ls -l 显示较全的文件信息,包括权限、用户、用户组等。      ls --color 显示文件及文件夹,并标有不同的...