暗色模式

Linux diff 与 patch:从文件对比到补丁应用与回退

技术教程
2026-08-22
5
0

Linux diff 与 patch:从文件对比到补丁应用与回退

本文要点
  • diff 用于逐行比较两个文件或目录,输出哪一种行被修改、新增或删除,退出码 0/1/2 分别代表无差异、有差异、出错。
  • 普通格式适合人读,diff -u 统一格式行列号紧凑、上下文清晰,是补丁文件的标准格式,也是 git diff 的同款格式。
  • diff -i / -w / -B / -y 分别忽略大小写、空白差异、空白行,或左右并排输出,适合对比不同版本的配置文件。
  • diff -rq 目录1 目录2 递归对比整个目录树,只列出有差异的文件名,是"部署前后对比"的利器。
  • patch 读取 diff 生成的补丁文件并应用到目标文件,patch -p0 --dry-run 先模拟不落盘,patch -R 一键回退。

开发或运维时,我们常遇到"两个文件到底差在哪"的问题:新版配置和线上配置相比改了什么?备份目录和当前目录多了哪些文件?别人给的改动怎么合进来?diffpatch 就是解决这类对比与合并问题的经典组合,也是 git 底层差异逻辑的来源。本文从最基础的对比讲起,一步步到生成补丁、应用补丁与回退。

diff 基础用法:输出差异与退出码

diff 的用法很直接:diff 文件1 文件2,把两个文件逐行比较并打印差异。比如 Linux 服务器上有两份 Nginx 风格的站点配置:

cat app.conf
server {
    listen 80;
    server_name example.com;
    root /var/www/example;
    index index.html;
}

app.conf.new 是后续要升级到的新版,改动了端口、增加了一行索引配置并开启了 access 日志。用 diff 一对比,差异一目了然:

diff app.conf app.conf.new

diff 基本用法输出格式

输出中的每一块都由三部分组成:

  • 2c2:块头。2 表示文件 1 的第 2 行,c 是 change(修改),后面的 2 表示文件 2 的第 2 行。字母除了 c 还有 a(append,新增)和 d(delete,删除)。
  • < 开头:文件 1(app.conf)里的内容。
  • > 开头:文件 2(app.conf.new)里的内容。

所以 2c2 下方的 < listen 80;> listen 8080; 意思是"第 2 行从 listen 80; 改成了 listen 8080;";5c5,6 表示第 5 行被替换成文件 2 的第 5、6 两行,即 index index.html; 换成了 index index.html index.php; 加上新增的 access_log off;

除了打印差异,diff 还会返回退出码,脚本里常据此判断文件是否一致:

  • 0:无差异(两文件完全相同)
  • 1:有差异
  • 2:出错(如文件不存在)
diff app.conf app.conf.new; echo "exit=$?"

输出末尾的 echo 会显示 exit=1,证明两文件确实不同。

常用对比选项:忽略大小写、空白与并排

对比文件时,我们经常只想关心"实质差异",而忽略无伤大雅的空白、大小写或空行。diff 为此准备了一组开关:

选项作用适用场景
-i忽略大小写差异对比受 Windows/Linux 换行风格差异影响的文本
-w忽略所有空白字符的差异配置对齐后空格数变了
-B忽略空行的增删排版时多加了空行
-y左右并排显示想直观看到逐列对应关系
-W N配合 -y 指定并排宽度并排输出按列对齐

实测:当 a.txtHello Worldb.txthello world(仅大小写不同)时:

diff -i a.txt b.txt

没有任何输出,同时 exit=0,说明 -i 成功忽略了大小写。同理,name=alice name = alice(空格分布不同)在 -w 下也视为相同。

这些选项可以组合使用,比如 diff -iwB app.conf app.conf.new 同时忽略大小写、空白与空行。但要留意:忽略条件放得越宽,"相同"的判断就越宽松,适合快速筛选,真正上补丁时还是要用完整对比。

diff -u 统一格式:补丁文件的标准格式

上面普通格式按块编号输出,机器好理解,但人读起来不连贯。diff -u(unified,统一格式)把差异放在完整上下文里展示,行号用 -+@@ 标注,这也是补丁文件(patch file)的事实标准:

diff -u app.conf app.conf.new

diff -u 统一格式

解读这份输出:

  • 前两行 --- app.conf+++ app.conf.new 分别是被对比的两个文件及其修改时间。
  • @@ -1,6 +1,7 @@ 是差异的定位:-1,6 指旧文件从第 1 行起共 6 行,+1,7 指新文件从第 1 行起共 7 行,数字反映的是文件行数的变化。
  • 不带前缀的行是"上下文"(两边共有,未改动)。
  • - 开头的是旧文件删掉的(listen 80;index index.html;)。
  • + 开头的是新文件加入的(listen 8080;index index.html index.php;access_log off;)。

-u 格式把"改动最小化"体现得很清楚:只显示真正变化的行及其附近上下文,这正是它能成为补丁标准的原因。用过的读者会发现,这份输出和 git diff 的格式如出一辙——Git 就是借用 unified 格式来展示差异的。

目录对比:diff -r,找出两棵树差在哪

对比单个文件很常见,但在运维场景里,更常见的是对比整个目录树:备份目录和当前目录是否一致?发布新版本后部署目录和预期差了哪些文件?diff -r 递归对比目录下所有文件,重大对比时建议加上 -q,只列出"哪个文件不同",而不是把每个文件的内容差异都打出来:

diff -rq conf_a conf_b

假设 conf_aconf_b 各有一套站点配置,其中 server.conf 在新版本里做了修改,而 app.conf 完全一致,-rq 会简洁地输出:

Files conf_a/server.conf and conf_b/server.conf differ

只有不一致的文件才会出现在结果里。若没有任何输出且退出码为 0,说明两个目录树逐字节一致——这正是"你的备份没问题"的强证据。实际应用中还常用到 diff -ru 生成目录级补丁,配合下文 patch 一次把多文件改动应用到另一台机器。

生成补丁:把 diff 输出存成文件

理解了 diff -u 后,生成补丁就是一步重定向:

diff -u app.conf app.conf.new > change.patch

重定向不会在终端打印任何东西,但 change.patch 文件里已经保存了完整的 unified 差异。这个文件就是"补丁"。它的好处是:只含改动最小集,体积小、易传输,任何完好的目标文件只要打上这份补丁就能变成新版本,非常适合跨机器分发配置或代码改动。

patch 应用补丁:dry-run 模拟、应用与验证

patch 程序负责读取补丁文件并应用到目标文件。它默认会尝试从补丁里 ---/+++ 后面的文件名定位目标,所以一般配合 -p0(不剥离路径前缀)在"相对当前目录能匹配到该文件名"的位置执行。

动手改文件之前,强烈建议先用 --dry-run 模拟一遍。它只报告将要做什么(checking file),不会真正修改文件。模拟通过后再正式应用:

patch -p0 --dry-run < change.patch && patch -p0 < change.patch

实测输出依次为:

checking file app.conf
patching file app.conf

应用后用 diff 验证一下目标文件是否和预期的新版一致:

diff app.conf app.conf.new && echo IDENTICAL

diff 无任何输出且跟随打印 IDENTICAL,说明 app.conf 已经被补丁改造成与 app.conf.new 完全相同的内容。整体流程截图:

patch 应用与验证

回退补丁:patch -R 一键撤销

如果打上补丁后发现不合预期,patch -R 会反向应用补丁,把目标文件恢复到应用前的样子,相当于是补丁的"撤销":

patch -R -p0 < change.patch

输出 patching file app.conf。再跑一遍 diff app.conf app.conf.new,差异又回来了,说明文件已还原。-R 之所以可行,是因为补丁文件里完整记录了每一行的增删,反向操作即可精确恢复。

小结:diff 与 patch 的日常分工

diff 负责"发现问题":单文件对比看细节、-u 看补丁格式、-rq 对比整个目录树;patch 负责"解决问题":--dry-run 先模拟、-p0 精确定位、-R 随时回退。两者配合,既能审计"改了什么",又能把改动安全地应用到目标环境。动手练习时,建议在测试目录里用自己改过的配置或代码反复演练,体会退出码、-u 行号与 -R 回退这几个关键点,很快就能上手。

参考资料:GNU diff 与 cmp 手册patch 命令文档(manpage)

发表评论

暂无评论,快来抢沙发吧!