用Python检测是否有新增和修改的文件
坐我隔壁的美女前端工程师,这几天总是疑神疑鬼,总是怀疑有人黑进她的电脑修改了她的文件,甚至可能在她的前端程序中植入了恶意代码。为此她过来找我帮忙,希望能帮她写一个文件检测程序,运行程序就能快速找出被修改过和新增的文件。
呃,这个需求我刚好也需要,想做很久了,但还是假装出勉为其难的样子,我跟她说做这个很麻烦,我又很忙,每天晚上都要加班到八九点,实在没空做这个,除非……除非你给我什么好处,嘿嘿嘿……最后,美女同意给我买一杯4元的蜜雪冰城柠檬水作为犒劳,哈哈,又成功骗了一杯柠檬水。
一、思路
1、价值4元的程序,我不想写得太复杂,暂不采用后台实时监控,需要美女前端自己手动运行 Python 程序检测。但不排除以后会升级为实时监控。
2、需要先生成一份“指纹档案”,记录要检测的目录中的每一个文件的路径、文件MD5、文件大小、创建日期、修改日期等,后面检测的时候,以这份“指纹档案”为基准进行对比。这份档案不会自动更新,一段时间后需要手动运行程序重新生成。
3、“指纹档案”需要用文件或数据库存储,考虑到前端工程师的电脑中一般没有安装 mysql 之类的数据库,所以我决定用 Excel 存储数据。
4、根据前面三点,这个程序需要4个文件:
① 文件 20131016.xlsx 是“指纹档案”文件,以日期作为版本号命名;
②文件 01.py 用于遍历目录并生成指纹档案;
③文件 02.py 用于遍历目录并与指纹档案对比,将发现的差异(新增和修改)输出;
④文件 functions.py 是自定义公共函数库,用于存放 01.py 和 02.py 的公共函数。
二、自定义公共函数库
新建 functions.py 文件,代码如下:
import os
import hashlib
from openpyxl import load_workbook
# 向 Excel 工作表追加一行数据
def append_row(file_path,sheet_name,data):
workbook = load_workbook(file_path)
sheet = workbook[sheet_name]
sheet.append(data)
workbook.save(file_path)
# 获取文件 MD5 值
def md5_hash_file(filename):
with open(filename, 'rb') as f:
md5 = hashlib.md5(f.read()).hexdigest()
return md5
# 递归遍历目录,返回一个文件路径列表
def traverse_dir(path):
r = []
for root, dirs, files in os.walk(path):
for file in files:
r.append(os.path.join(root,file))
return r
# 获取文件创建时间、修改时间、文件大小
def get_file_info(file_path):
info = {}
# 创建时间
info['add_time'] = os.path.getctime(file_path)
# 修改时间
info['edit_time'] = os.path.getmtime(file_path)
# 文件大小 (字节)
info['file_size'] = os.path.getsize(file_path)
return info
append_row() 函数用于向 Excel 文件追加一行数据,参数 data 是要追加的行数据列表。
md5_hash_file() 函数用于计算文件的 MD5 值。
traverse_dir() 函数递归遍历目录,返回一个文件路径列表。
get_file_info() 函数用于获取文件创建时间、修改时间、文件大小,时间以时间戳表示。
三、创建“指纹档案”
首先需要创建一个空白的 Excel 文档,扩展名为 xlsx 。在 windows 系统中,一般在桌面空白处点右键,新建“Microsoft Excel 工作表”即可。将其命名为 20131016.xlsx,这是以日期作为版本号,你也可以自主采用其它命名方案。
然后新建 01.py 文件:
from functions import append_row, md5_hash_file, traverse_dir, get_file_info
# 要检测的文件夹(目录)
target = 'E:\workspace\python'
# 设置文件名和工作表名,大小写敏感
file_path = '20131016.xlsx'
sheet_name = 'Sheet1'
# 表头
data = ['文件路径','文件MD5','文件旧MD5','文件大小','创建日期','修改日期']
append_row(file_path,sheet_name,data)
files = traverse_dir(target)
for file in files:
print(file)
info = get_file_info(file)
data = [file,md5_hash_file(file),'-',info['file_size'],info['add_time'],info['edit_time']]
append_row(file_path,sheet_name,data)
这段代码首先调用 append_row() 函数向 Excel 文件中添加了一个表头,然后使用 traverse_dir() 函数递归遍历目标目录,获得一个文件列表 files。循环处理 files 列表,使用 get_file_info() 函数获取每个文件的属性信息,将这些信息拼接成一行数据 data,调用 append_row() 将行数据追加写入 Excel 文档。
运行这段代码,可以看到 Excel 文档里已经有了内容,可能多达上千行。对于同一个 Excel 文件,不能重复运行 01.py,如果要重新生成指纹档案,须先重新创建一个空白的 Excel 文档。
指纹档案不会自动更新,用户须根据需要每过一段时间重新运行 01.py 程序生成新的指纹档案。
四、检测文件
在“指纹档案”创建一段时间之后,需要运行一段检测程序来查找新增和修改的文件。
02.py 文件:
import time
from openpyxl import load_workbook
from functions import md5_hash_file, traverse_dir, get_file_info
# 要检测的文件夹(目录)
target = 'E:\workspace\python'
# 设置文件名和工作表名,大小写敏感
file_path = '20131016.xlsx'
sheet_name = 'Sheet1'
# 加载 Excel 文件
workbook = load_workbook(file_path)
# 选择工作表
sheet = workbook[sheet_name]
new_files = []
edited = []
t = 1
files = traverse_dir(target)
for file in files:
is_new = True
print('检查文件' + str(t) + ': ' + file)
t = t + 1
info = get_file_info(file)
file_md5 = md5_hash_file(file)
rows = sheet.iter_rows(min_row=2, max_row=sheet.max_row, min_col=1, max_col=sheet.max_column, values_only=True)
for row in rows:
if row[0] == file:
is_new = False
if round(info['edit_time']) > round(row[5]):
# if file_md5 != row[1]:
edit_time = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(info['edit_time']))
edited.append([file,edit_time])
if is_new == True:
add_time = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(info['add_time']))
new_files.append([file,add_time])
print('新增的文件:')
for file in new_files:
print(file)
print('修改过的文件:')
for file in edited:
print(file)
在这段程序中,首先使用 openpyxl 库的 load_workbook() 函数加载 Excel 文件,并选择工作表。调用 traverse_dir() 函数获取目标检测目录的文件列表 files,然后循环处理 files,在循环中使用 sheet.iter_rows() 获取 Excel 工作表中从第2行起的所有数据,得到的是一个二维列表 rows,继续循环处理 rows。如果 row[0] == file,说明 file 不是新文件;如果 info['edit_time'] 大于 row[5],说明文件 file 的修改时间晚于指纹档案中记录的时间,即说明该文件被修改过,要注意 info['edit_time'] 和 row[5] 要用 round()函数取整,否则可能出现小数误差。
将新文件的路径和创建时间组成列表,追加到 new_files 列表;将有改动的文件的路径和修改时间组成列表,追加到 edited 列表。最后打印这两个列表。
这段程序中虽然调用 md5_hash_file() 函数获得一个 MD5 值,但没有使用该值,这里只作为备用,在一些比较严格的场景中,需要通过 MD5 来判断文件是否被修改,可以将
if round(info['edit_time']) > round(row[5]):
改为:
if file_md5 != row[1]:
至此,检测文件是否有新增和修改的 Python 程序就完成了。