霍克升级秘籍:从文件对比中探寻运维之道(Python)
关注我们
本文内容仅供学习参考,不足错误之处,还请多多指正,如果喜欢我们,请关注我们,你的支持就是我们最大的动力。
需求来源
霍克是一名软件实施工程师,每次版本升级时,虽然会提供升级相关的清单,但不会详细说明具体的改动点。为了能够更有针对性地验证和测试系统,霍克希望对比版本升级前后的文件,了解升级前后版本的变动情况。这样他就能做到心中有数,上线时更有底气。在上线后出现问题时,也能更快速地定位问题。
此时小霍可以借助一些工具来显示对比,比如使用Beyond Compare、Notepad++插件比对等,但是由于版本升级的文件众多,它们不能实现批量的文件对比,因此小霍同学,需要一个方案能实现如下需求:
- 批量对比版本升级前后的文件,包括代码、脚本和数据文件等。
- 对于相同文件(相同文件指同目录、同名,前后版本都存在)差异部分,能明确提示出增加、删除、修改的内容
- 能够对比出前后版本,新增了哪些文件、删除了哪些文件
- 支持对目录下所有文件进行对比,包括子文件夹
实现方案
通过查询发现,Python 中difflib模块,可以实现如上需求,该库为Python 标准库,无需安装。difflib模块主要用于对比文本之间的差异,并支持输出可读性比较强的HTML文档,基于difflib模块的开发逻辑如下:
- 通过glob模块获取两个目录下的所有文件列表,包括子文件夹
- 编写get_file_info()方法来对获取的文件列表进行处理
- 编写compare_file()方法来实现单个文件对比,核心代码为difflib模块的ndiff()方法,该方法用来比较两个字符串的内容并返回它们之间的差异
- 编写compare_file_batch方法来实现批量对比。
实现代码
# -*- coding: utf-8 -*-
"""
@author: MagicYang
@date: 20231117
@Tips: 关注朕大钱呀,微信公众号
@desc: 批量对比差异文件
"""
import glob, json, os, difflib
from src.tools.SqliteTool import SqliteOper
from loguru import logger
class FileCheckTool:
def __init__(self):
"构造函数"
self.dir1_path = r"C:\Users\Desktop\test\compare1\file" # 指定文件夹路径
self.dir2_path = r"C:\Users\Desktop\test\compare2\file" # 指定文件夹路径
self.file1_list = glob.glob(self.dir1_path + r"\**\*", recursive=True) # 获取文件夹下所有文件路径名
self.file2_list = glob.glob(self.dir2_path + r"\**\*", recursive=True) # 获取文件夹下所有文件路径名
self.split_str = r"\file" # 切割符
self.file1s_info_dict = self.get_file_info(self.file1_list)
self.file2s_info_dict = self.get_file_info(self.file2_list)
def get_file_info(self, file_list):
"""获取文件信息"""
temp_info_dict = {}
for file in file_list:
# 判断是否为文件
if os.path.isfile(file):
temp_info_dict[file.split(self.split_str)[1]] = {
"abs_path": file, # 文件绝对路径
"type": file.split(".")[-1], # 文件类型
}
return temp_info_dict
def compare_file_batch(self):
"""批量对比文件"""
file1_key = set(self.file1s_info_dict.keys())
file2_key = set(self.file2s_info_dict.keys())
# file1比file2多的文件
file1_more_file2 = list(file1_key - file2_key)
# file2比file1多的文件
file2_more_file1 = list(file2_key - file1_key)
# file2与file1两者的交集
file2_cross_file1 = list(file1_key.intersection(file2_key))
sqlite_opr = SqliteOper(close_flag=1)
sqlite_opr.delete_sql("delete from file_diff_res where 1=1")
inser_template_sql = """insert into file_diff_res(file_key,file_name,file1_abs_path,file2_abs_path,is_diff,diff_detail) values(?,?,?,?,?,?) """
for i in file1_more_file2:
file_key = i
file_name = str(i).split('/')[-1]
file1_abs_path = self.file1s_info_dict.get(i)['abs_path']
file2_abs_path = self.file2s_info_dict.get(i, '--')
is_diff = True
diff_detail = ""
sqlite_opr.insert_one_args(inser_template_sql,
[file_key, file_name, file1_abs_path, file2_abs_path, is_diff, diff_detail])
for i in file2_more_file1:
file_key = i
file_name = str(i).split('/')[-1]
file1_abs_path = self.file1s_info_dict.get(i, '--')
file2_abs_path = self.file2s_info_dict.get(i)['abs_path']
is_diff = True
diff_detail = ""
sqlite_opr.insert_one_args(inser_template_sql,
[file_key, file_name, file1_abs_path, file2_abs_path, is_diff, diff_detail])
# 处理两边数据差异部分
for i in file2_cross_file1:
abs_file1 = self.file1s_info_dict[i]["abs_path"]
abs_file2 = self.file2s_info_dict[i]["abs_path"]
self.compare_file(abs_file1, abs_file2, i)
sqlite_opr.close_conn()
def compare_file(self, file1_path, file2_path, diff_res_name):
"""对比文件"""
try:
with open(file1_path, "r", encoding="utf-8") as f:
content1 = f.read().splitlines() # 读取之后进行行分割
f.close()
with open(file2_path, "r", encoding="utf-8") as f:
content2 = f.read().splitlines() # 读取之后进行行分割
f.close()
except BaseException as e:
logger.info(file1_path)
logger.info(file2_path)
logger.info(e)
sqlite_opr = SqliteOper()
diff = difflib.ndiff(content1, content2) # 创建一个diff对象
temp_diff_res = []
is_diff = False
for i in diff:
if i[0] in ("?", "+", "-"):
logger.info(i.replace("\n", ""))
temp_diff_res.append(i)
is_diff = True
temp_diff_res_str = "\n".join(temp_diff_res)
inser_template_sql = """insert into file_diff_res(file_key,file_name,file1_abs_path,file2_abs_path,is_diff,diff_detail) values(?,?,?,?,?,?) """
sqlite_opr.insert_one_args(inser_template_sql,
[diff_res_name, str(diff_res_name).split("\\")[-1], file1_path, file2_path, is_diff,
temp_diff_res_str])
if __name__ == "__main__":
fct = FileCheckTool()
fct.compare_file_batch()
实现验证
我们现在需要对如下目录结构的文件进行对比:
两者异同如下:
- compare2比compare1多了一个ccc.sql文件
- aaa.json 文件两边一致,内容如下:
compare1
compare2
- ccc.json文件两边有差异
compare1
compare2
结果如下,与需求一致:
结果分析如下:
文件:aaa.json
结果:
# 空格表示两个文件行记录没有差异
aaa
aaa
aaa
aaa
aaa
=================================
文件:ccc.json
结果:
{
# - 表示第一个文件与第二个文件的差异部分
- "name":"Magic",
# ?表示两个文件行存在差异
? ^ ^^^
# + 表示第二个文件与第一个文件的差异部分
+ "name":"Zhao",
? ^^ ^
"sex":"male",
- "age":"18",
? -
+ "age":"21",
? +
}
=================================
结尾
我们下期见,可以添加yc_505505微信号,进入学习交流群,我们一起学习、分享、成长。
你都这么好看了,可以给我个关注不~