1.filecmp模塊介紹
當我們進行代碼審計或校驗備份結(jié)果時,往往需要檢查原始與目 標目錄的文件一致性,Python的標準庫已經(jīng)自帶了滿足此需求的模塊 filecmp。filecmp可以實現(xiàn)文件、目錄、遍歷子目錄的差異對比功能。比 如報告中輸出目標目錄比原始多出的文件或子目錄,即使文件同名也會 判斷是否為同一個文件(內(nèi)容級對比)等,Python 2.3或更高版本默認 自帶filecmp模塊,無需額外安裝,下面進行詳細介紹。
?
2.模塊常用方法說明
filecmp提供了三個操作方法,分別為cmp(單文件對比)、 cmpfiles(多文件對比)、dircmp(目錄對比),下面逐一進行介紹: ·
單文件對比:
采用filecmp.cmp(f1,f2[,shallow])方法,比較文 件名為f1和f2的文件,相同返回True,不相同返回False,shallow默認為 True,意思是只根據(jù)os.stat()方法返回的文件基本信息進行對比,比 如最后訪問時間、修改時間、狀態(tài)改變時間等,會忽略文件內(nèi)容的對 比。當shallow為False時,則os.stat()與文件內(nèi)容同時進行校驗。
示例:比較單文件的差異
>>>
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目錄給定的文件清單。該方法返回文件 名的三個列表,分別為匹配、不匹配、錯誤。匹配為包含匹配的文件的 列表,不匹配反之,錯誤列表包括了目錄不存在文件、不具備讀權(quán)限或 其他原因?qū)е碌牟荒鼙容^的文件清單
示例:dir1與dir2目錄中指定文件清單對比。
兩目錄下文件的md5信息如下,其中f1、f2文件匹配;f3不匹配; f4、f5對應目錄中不存在,無法比較。
創(chuàng)建測試文件:
[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對比的結(jié)果如下,符合我們的預期。
f1、f2文件匹配;f3不匹配; f4、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]])類創(chuàng)建一個目錄 比較對象,其中a和b是參加比較的目錄名。ignore代表文件名忽略的列 表,并默認為['RCS' , 'CVS' , 'tags'];hide代表隱藏的列表,默認為 [os.curdir,os.pardir]。dircmp類可以獲得目錄比較的詳細信息,如只有 在a目錄中包括的文件、a與b都存在的子目錄、匹配的文件等,同時支 持遞歸。
?dircmp提供了三個輸出報告的方法: ·
report(),比較當前指定目錄中的內(nèi)容; ·
report_partial_closure(),比較當前指定目錄及第一級子目錄中 的內(nèi)容; ·
report_full_closure(),遞歸比較所有指定目錄的內(nèi)容。
為輸出更加詳細的比較結(jié)果,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的目錄差異
通過調(diào)用dircmp()方法實現(xiàn)目錄差異對比功能,同時輸出目錄 對比對象所有屬性信息。
【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
'
])
#
目錄比較
#
輸出對比結(jié)果數(shù)據(jù)報表,詳細說明請參考filecmp類方法及屬性信息
print
'
--------------report比較當前指定目錄中的內(nèi)容----------------
'
dir_obj.report()
#
比較當前指定目錄中的內(nèi)容;
print
'
--------------report_partial_closure比較當前指定目錄及第一級子目錄中 的內(nèi)容----------------
'
dir_obj.report_partial_closure()
#
比較當前指定目錄及第一級子目錄中 的內(nèi)容;
print
'
--------------report_full_closure遞歸比較所有指定目錄的內(nèi)容----------------
'
dir_obj.report_full_closure()
#
遞歸比較所有指定目錄的內(nèi)容。
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比較當前指定目錄中的內(nèi)容----------------
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比較當前指定目錄及第一級子目錄中 的內(nèi)容----------------
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遞歸比較所有指定目錄的內(nèi)容----------------
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方法對更新項進行復制,最終保持一致狀 態(tài)。詳細源碼如下:
【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):
#
遞歸獲取更新函數(shù)
dircomp=
filecmp.dircmp(dir1,dir2)
only_in_one
=dircomp.left_only
#
源目錄新文件或目錄
diff_in_one=dircomp.diff_files
#
不匹配文件,源目錄文件已發(fā)生變化
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):
#
如果差異路徑為目錄且不存在,則在備份目錄中創(chuàng)建
if
not
os.path.exists(destination_dir):
os.makedirs(destination_dir)
createdir_bool
=True
#
再次調(diào)用compareme函數(shù)標記
if
createdir_bool:
#
重新調(diào)用compareme函數(shù),重新遍歷新創(chuàng)建目錄的內(nèi)容
destination_files=
[]
source_files
=
[]
source_files
=compareme(dir1,dir2)
#
調(diào)用compareme函數(shù)
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:
[] #再次運行時已經(jīng)沒有更新項了
?
更多文章、技術(shù)交流、商務合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

