python運維開發經常使用模塊(5)文件目錄對比模塊filecmp

1.filecmp模塊介紹

當咱們進行代碼審計或校驗備份結果時,每每須要檢查原始與目 標目錄的文件一致性,Python的標準庫已經自帶了知足此需求的模塊 filecmp。filecmp能夠實現文件、目錄、遍歷子目錄的差別對比功能。比 如報告中輸出目標目錄比原始多出的文件或子目錄,即便文件同名也會 判斷是否爲同一個文件(內容級對比)等,Python 2.3或更高版本默認 自帶filecmp模塊,無需額外安裝,下面進行詳細介紹。python

 

2.模塊經常使用方法說明

filecmp提供了三個操做方法,分別爲cmp(單文件對比)、 cmpfiles(多文件對比)、dircmp(目錄對比),下面逐一進行介紹: ·nginx

單文件對比:

採用filecmp.cmp(f1,f2[,shallow])方法,比較文 件名爲f1和f2的文件,相同返回True,不相同返回False,shallow默認爲 True,意思是隻根據os.stat()方法返回的文件基本信息進行對比,比 如最後訪問時間、修改時間、狀態改變時間等,會忽略文件內容的對 比。當shallow爲False時,則os.stat()與文件內容同時進行校驗。app

示例:比較單文件的差別ide

>>> import filecmp
>>> from filecmp import cmp
>>> cmp('/home/yhl/devpython/part2/nginx.conf.v1','/home/yhl/devpython/part2/nginx.conf.v2')
False
>>> cmp('/home/yhl/devpython/part2/nginx.conf.v1','/home/yhl/devpython/part2/nginx.conf.v0')
True

多文件對比:

採用filecmp.cmpfiles(dir1,dir2,common[, shallow])方法,對比dir1與dir2目錄給定的文件清單。該方法返回文件 名的三個列表,分別爲匹配、不匹配、錯誤。匹配爲包含匹配的文件的 列表,不匹配反之,錯誤列表包括了目錄不存在文件、不具有讀權限或 其餘緣由致使的不能比較的文件清單函數

示例:dir1與dir2目錄中指定文件清單對比。測試

兩目錄下文件的md5信息以下,其中f一、f2文件匹配;f3不匹配; f四、f5對應目錄中不存在,沒法比較。spa

建立測試文件:code

[yhl@myhost part2]$ mkdir dir1
[yhl@myhost part2]$ mkdir dir2
[yhl@myhost part2]$ cd dir1
[yhl@myhost dir1]$ echo f1 >f1
[yhl@myhost dir1]$ echo f2 >f2
[yhl@myhost dir1]$ echo f3 >f3
[yhl@myhost dir1]$ echo f5 >f5
[yhl@myhost dir1]$ cd ../dir2
[yhl@myhost dir2]$ echo f1 >f1
[yhl@myhost dir2]$ echo f2 >f2
[yhl@myhost dir2]$ echo f03 >f3
[yhl@myhost dir2]$ echo f4 >f4

對比dir1和dir2目錄下文件的md5對象

[yhl@myhost dir1]$ md5sum *
2b1abc6b6c5c0018851f9f8e6475563b  f1
575c5638d60271457e54ab7d07309502  f2
3385b5d27d4c2923e9cde7ea53f28e2b  f3
4c89aa650e394e642f6a84df6cdb08a4  f5
[yhl@myhost dir1]$ cd -
/home/yhl/devpython/part2/dir2
[yhl@myhost dir2]$ md5sum *
2b1abc6b6c5c0018851f9f8e6475563b  f1
575c5638d60271457e54ab7d07309502  f2
287df2010a083579b709b63445a32cc3  f3
5f3022d3a5cbcbf30a75c33ea39b2622  f4

使用cmpfiles對比的結果以下,符合咱們的預期。blog

f一、f2文件匹配;f3不匹配; f四、f5對應目錄中不存在,沒法比較。

>>> from filecmp import cmpfiles
>>> cmpfiles("/home/yhl/devpython/part2/dir1","/home/yhl/devpython/part2/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(),遞歸比較全部指定目錄的內容。

爲輸出更加詳細的比較結果,dircmp類還提供瞭如下屬性: ·

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()方法實現目錄差別對比功能,同時輸出目錄 對比對象全部屬性信息。

【simple3.py】

#!/usr/bin/python
#_*_coding:utf-8_*_
#****************************************************************#
# ScriptName: simple3.py
# Author: BenjaminYang
# Create Date: 2019-05-13 17:44
# Modify Author: BenjaminYang
# Modify Date: 2019-05-13 17:44
# Function: 
#***************************************************************#
import filecmp
a="/home/yhl/devpython/part2/testfile/dir1" #定義左目錄
b="/home/yhl/devpython/part2/testfile/dir2" #定義右目錄
dir_obj=filecmp.dircmp(a,b,['test.py'])#目錄比較

#輸出對比結果數據報表,詳細說明請參考filecmp類方法及屬性信息
print '--------------report比較當前指定目錄中的內容----------------'
dir_obj.report() #比較當前指定目錄中的內容;
print '--------------report_partial_closure比較當前指定目錄及第一級子目錄中 的內容----------------'
dir_obj.report_partial_closure()#比較當前指定目錄及第一級子目錄中 的內容;
print '--------------report_full_closure遞歸比較全部指定目錄的內容----------------'
dir_obj.report_full_closure() #遞歸比較全部指定目錄的內容。
print '--------------left_list左目錄中的文件及目錄列表----------------'
print "left_list: "+ str(dir_obj.left_list) #左目錄中的文件及目錄表; 
print '--------------right_list右目錄中的文件及目錄列表----------------'
print "right_list: "+ str(dir_obj.right_list) #右目錄中的文件及目錄列表;
print '--------------common兩邊目錄共同存在的文件或目錄----------------'
print "common: "+ str(dir_obj.common)     #兩邊目錄共同存在的文件或目錄
print '--------------left_only只在左目錄中的文件或目錄----------------'
print "left_only: "+ str(dir_obj.left_only)  #只在左目錄中的文件或目錄; 
print '--------------right_only右目錄中的文件及目錄列表----------------'
print "right_only: "+ str(dir_obj.right_only)  #只在右目錄中的文件或目錄;
print '--------------common_dirs兩邊目錄都存在的子目錄----------------'
print "common_dirs: "+ str(dir_obj.common_dirs) #兩邊目錄都存在的子目錄;
print '--------------common_files兩邊目錄都存在的子文件----------------'
print "common_files: "+ str(dir_obj.common_files) #兩邊目錄都存在的子文件;
print '--------------common_funny兩邊目錄都存在的子目錄(不一樣目錄類型或os.stat()記錄的錯誤----------------'
print "common_funny: "+ str(dir_obj.common_funny) #兩邊目錄都存在的子目錄(不一樣目錄類型或os.stat()記錄的錯誤);
print '--------------same_files匹配相同的文件----------------'
print "same_file: "+ str(dir_obj.same_files) #匹配相同的文件;
print '--------------diff_files不匹配的文件----------------'
print "diff_files: "+ str(dir_obj.diff_files) #不匹配的文件;
print '--------------funny_files#兩邊目錄中都存在,但沒法比較的文件----------------'
print "funny_files: "+ str(dir_obj.funny_files) #兩邊目錄中都存在,但沒法比較的文件

代碼輸出:

[yhl@myhost testfile]$ python simple3.py
--------------report比較當前指定目錄中的內容----------------
diff /home/yhl/devpython/part2/testfile/dir1 /home/yhl/devpython/part2/testfile/dir2
Only in /home/yhl/devpython/part2/testfile/dir1 : ['f4']
Only in /home/yhl/devpython/part2/testfile/dir2 : ['aa', 'f5']
Identical files : ['f1', 'f2']
Differing files : ['f3']
Common subdirectories : ['a']
--------------report_partial_closure比較當前指定目錄及第一級子目錄中 的內容----------------
diff /home/yhl/devpython/part2/testfile/dir1 /home/yhl/devpython/part2/testfile/dir2
Only in /home/yhl/devpython/part2/testfile/dir1 : ['f4']
Only in /home/yhl/devpython/part2/testfile/dir2 : ['aa', 'f5']
Identical files : ['f1', 'f2']
Differing files : ['f3']
Common subdirectories : ['a']

diff /home/yhl/devpython/part2/testfile/dir1/a /home/yhl/devpython/part2/testfile/dir2/a
Identical files : ['a1']
Common subdirectories : ['b']
--------------report_full_closure遞歸比較全部指定目錄的內容----------------
diff /home/yhl/devpython/part2/testfile/dir1 /home/yhl/devpython/part2/testfile/dir2
Only in /home/yhl/devpython/part2/testfile/dir1 : ['f4']
Only in /home/yhl/devpython/part2/testfile/dir2 : ['aa', 'f5']
Identical files : ['f1', 'f2']
Differing files : ['f3']
Common subdirectories : ['a']

diff /home/yhl/devpython/part2/testfile/dir1/a /home/yhl/devpython/part2/testfile/dir2/a
Identical files : ['a1']
Common subdirectories : ['b']

diff /home/yhl/devpython/part2/testfile/dir1/a/b /home/yhl/devpython/part2/testfile/dir2/a/b
Identical files : ['b1', 'b2', 'b3']
--------------left_list左目錄中的文件及目錄列表----------------
left_list: ['a', 'f1', 'f2', 'f3', 'f4']
--------------right_list右目錄中的文件及目錄列表----------------
right_list: ['a', 'aa', 'f1', 'f2', 'f3', 'f5']
--------------common兩邊目錄共同存在的文件或目錄----------------
common: ['a', 'f1', 'f2', 'f3']
--------------left_only只在左目錄中的文件或目錄----------------
left_only: ['f4']
--------------right_only右目錄中的文件及目錄列表----------------
right_only: ['aa', 'f5']
--------------common_dirs兩邊目錄都存在的子目錄----------------
common_dirs: ['a']
--------------common_files兩邊目錄都存在的子文件----------------
common_files: ['f1', 'f2', 'f3']
--------------common_funny兩邊目錄都存在的子目錄(不一樣目錄類型或os.stat()記錄的錯誤----------------
common_funny: []
--------------same_files匹配相同的文件----------------
same_file: ['f1', 'f2']
--------------diff_files不匹配的文件----------------
diff_files: ['f3']
--------------funny_files#兩邊目錄中都存在,但沒法比較的文件----------------
funny_files: []

 實踐:校驗源與備份目錄差別

有時候咱們沒法確認備份目錄與源目錄文件是否保持一致,包括 源目錄中的新文件或目錄、更新文件或目錄有無成功同步,按期進行校 驗,沒有成功則但願有針對性地進行補備份。本示例使用了filecmp模塊 的left_only、diff_files方法遞歸獲取源目錄的更新項,再經過 shutil.copyfile、os.makedirs方法對更新項進行復制,最終保持一致狀 態。詳細源碼以下:

【simple4.py】

#!/usr/bin/python
#_*_coding:utf-8_*_
#****************************************************************#
# ScriptName: simple4.py
# Author: BenjaminYang
# Create Date: 2019-05-14 17:48
# Modify Author: BenjaminYang
# Modify Date: 2019-05-14 17:48
# Function: 
#***************************************************************#
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,itme)
            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()

 

[yhl@myhost testfile]$ python simple4.py dir1 dir2
update item: 
['/home/yhl/devpython/part2/testfile/dir1/f4', '/home/yhl/devpython/part2/testfile/dir1/f3']
[yhl@myhost testfile]$ python simple4.py dir1 dir2
update item: 
[] #再次運行時已經沒有更新項了
相關文章
相關標籤/搜索