程序员文章、书籍推荐和程序员创业信息与资源分享平台

网站首页 > 技术文章 正文

霍克升级秘籍:从文件对比中探寻运维之道(Python)

hfteth 2025-06-23 19:17:22 技术文章 1 ℃

关注我们

本文内容仅供学习参考,不足错误之处,还请多多指正,如果喜欢我们,请关注我们,你的支持就是我们最大的动力。

需求来源

霍克是一名软件实施工程师,每次版本升级时,虽然会提供升级相关的清单,但不会详细说明具体的改动点。为了能够更有针对性地验证和测试系统,霍克希望对比版本升级前后的文件,了解升级前后版本的变动情况。这样他就能做到心中有数,上线时更有底气。在上线后出现问题时,也能更快速地定位问题。

此时小霍可以借助一些工具来显示对比,比如使用Beyond CompareNotepad++插件比对等,但是由于版本升级的文件众多,它们不能实现批量的文件对比,因此小霍同学,需要一个方案能实现如下需求:

  1. 批量对比版本升级前后的文件,包括代码、脚本和数据文件等。
  2. 对于相同文件(相同文件指同目录、同名,前后版本都存在)差异部分,能明确提示出增加、删除、修改的内容
  3. 能够对比出前后版本,新增了哪些文件、删除了哪些文件
  4. 支持对目录下所有文件进行对比,包括子文件夹

实现方案

通过查询发现,Python 中difflib模块,可以实现如上需求,该库为Python 标准库,无需安装。difflib模块主要用于对比文本之间的差异,并支持输出可读性比较强的HTML文档,基于difflib模块的开发逻辑如下:

  1. 通过glob模块获取两个目录下的所有文件列表,包括子文件夹
  2. 编写get_file_info()方法来对获取的文件列表进行处理
  3. 编写compare_file()方法来实现单个文件对比,核心代码为difflib模块的ndiff()方法,该方法用来比较两个字符串的内容并返回它们之间的差异
  4. 编写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()

实现验证

我们现在需要对如下目录结构的文件进行对比:

两者异同如下:

  1. compare2比compare1多了一个ccc.sql文件
  2. aaa.json 文件两边一致,内容如下:

compare1

compare2

  1. 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微信号,进入学习交流群,我们一起学习、分享、成长。

你都这么好看了,可以给我个关注不~

Tags:

最近发表
标签列表