Linux diff 与 patch 命令完全指南:文件比较与补丁管理
在 Linux 系统管理和软件开发中,文件比较和补丁管理是两项基础而强大的技能。diff 命令用于逐行比较文件差异,patch 命令则根据差异文件(补丁)将变更应用到目标文件。这对组合是 Git 等版本控制系统的底层基石,也是运维人员日常排查配置变更、管理代码更新的得力工具。
本文将全面介绍 diff 和 patch 的核心用法、输出格式解读、实战场景以及高级技巧。
一、diff 命令详解
1.1 基本语法
diff 比较两个文件,输出它们之间的差异行。如果两个文件完全相同,则无输出(或返回空)。
1.2 三种输出格式
diff 支持三种差异输出格式,其中**统一格式(unified)**是最常用和最易读的。
1.2.1 正常格式(normal)
默认输出,直接显示需要修改的行和操作:
1
| diff /etc/nginx/nginx.conf.bak /etc/nginx/nginx.conf
|
输出示例:
1 2 3 4 5 6
| 2c2 < sendfile off;
> sendfile on; 5a6 > gzip on;
|
2c2:文件1的第2行被替换为文件2的第2行
5a6:在文件1的第5行之后追加文件2的第6行
< 开头:文件1的内容
> 开头:文件2的内容
---:两部分的分隔线
操作符说明:
| 操作符 | 含义 | 示例 |
a | 追加(append) | 5a6 — 第5行后追加 |
c | 替换(change) | 2c2 — 第2行替换 |
d | 删除(delete) | 3d2 — 删除第3行 |
1.2.2 上下文格式(context,-c)
显示差异行周围的上下文(默认3行),适合查看变更的上下文:
1
| diff -c nginx.conf.bak nginx.conf
|
输出示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
*** 1,6 **** user www-data; worker_processes auto; ! sendfile off; keepalive_timeout 65; include /etc/nginx/conf.d/*.conf; --- 1,7 ---- user www-data; worker_processes auto; ! sendfile on; keepalive_timeout 65; include /etc/nginx/conf.d/*.conf; + gzip on;
|
*** 1,6 ****:文件1的第1-6行
--- 1,7 ----:文件2的第1-7行
! 开头:有变更的行
+ 开头:新增的行
- 开头:删除的行
1.2.3 统一格式(unified,-u)⭐推荐
合并上下文和变更标记,更紧凑易读。这是 Git diff 使用的格式,也是日常最推荐的方式:
1
| diff -u nginx.conf.bak nginx.conf
|
输出示例:
1 2 3 4 5 6 7 8 9 10
|
@@ -1,6 +1,7 @@ user www-data; worker_processes auto; -sendfile off; +sendfile on; keepalive_timeout 65; include /etc/nginx/conf.d/*.conf; +gzip on;
|
@@ -1,6 +1,7 @@:文件1第1-6行 → 文件2第1-7行
- 删除的行
+ 新增的行
- 无前缀的行:共享的上下文
推荐使用 -u 选项——输出简洁、包含上下文、直接可用于 patch 命令。
1.3 常用选项速查
| 选项 | 说明 | 示例 |
-u | 统一格式输出(推荐) | diff -u a.txt b.txt |
-c | 上下文格式输出 | diff -c a.txt b.txt |
-i | 忽略大小写差异 | diff -i a.txt b.txt |
-w | 忽略空白字符差异 | diff -w a.txt b.txt |
-b | 忽略空白数量变化 | diff -b a.txt b.txt |
-r | 递归比较目录 | diff -r dir1 dir2 |
-q | 仅报告文件是否不同(安静模式) | diff -q a.txt b.txt |
-N | 将缺失文件视为空文件(配合 -r 使用) | diff -ruN dir1 dir2 |
-x PAT | 排除匹配模式的文件 | diff -r -x node_modules d1 d2 |
--color | 彩色输出差异 | diff -u --color a.txt b.txt |
1.4 递归比较目录
这是 diff 最强大的运维场景之一——比较两个目录树的差异:
1 2 3 4 5 6 7 8 9 10 11
| diff -rq /etc/nginx/sites-enabled/ /etc/nginx/sites-available/
diff -ruN /etc/nginx/ /backup/nginx/
diff -rq /var/www/html/ /var/www/html_bak/
diff -ruN --exclude=cache --exclude=logs app/ app_new/
|
1.5 重定向差异输出到文件
1 2 3 4 5
| diff -u nginx.conf.bak nginx.conf > nginx.patch
diff -ruN config/ config_new/ > config-upgrade.patch
|
二、patch 命令详解
patch 命令将 diff 生成的补丁文件应用到目标文件或目录,实现自动化变更。
2.1 基本语法
1 2
| patch [选项] < 补丁文件 patch 目标文件 < 补丁文件
|
2.2 应用补丁的两种方式
方式一:预先指定目标
1 2
| diff -u origin.conf modified.conf > change.patch patch origin.conf < change.patch
|
执行后 origin.conf 被修改为与 modified.conf 一致。
方式二:补丁内包含路径信息(推荐)
1 2 3 4 5
| diff -u /etc/nginx/nginx.conf.orig /etc/nginx/nginx.conf > nginx.patch
patch -p0 < nginx.patch
|
-pN 参数指定剥离路径前缀的层数:
| 参数 | 路径处理 | 示例文件路径 |
-p0 | 使用完整路径 | /etc/nginx/nginx.conf |
-p1 | 剥离第一级 | etc/nginx/nginx.conf |
-p3 | 剥离三级 | nginx.conf |
2.3 patch 常用选项
| 选项 | 说明 | 示例 |
-pN | 剥离路径前缀 N 级 | patch -p1 < patch.diff |
-R | 反向应用补丁(回滚) | patch -R < patch.diff |
--dry-run | 试运行(不实际修改,检查兼容性) | patch --dry-run < patch.diff |
-b | 备份原文件(生成 .orig 文件) | patch -b < patch.diff |
-B PREFIX | 备份文件前缀 | patch -B .bak~ < patch.diff |
-d DIR | 在指定目录下执行 patching | patch -d /etc/nginx < patch.diff |
-i FILE | 从文件读取补丁 | patch -i change.patch |
-E | 应用补丁后删除空文件 | patch -E < patch.diff |
-s | 静默模式(仅输出错误) | patch -s < patch.diff |
2.4 安全先行:dry-run 试运行
在实际应用补丁之前,务必先试运行:
1 2
| patch --dry-run < change.patch
|
如果输出类似 Hunk #1 FAILED at 1.,说明补丁与当前文件不匹配,需要手动检查。
三、实战场景
场景 1:配置文件变更管理
运维人员修改 Nginx 配置前先备份,然后生成补丁方便回滚:
1 2 3 4 5 6 7 8
| cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.$(date +%Y%m%d)
diff -u /etc/nginx/nginx.conf.$(date +%Y%m%d) /etc/nginx/nginx.conf > ~/nginx-change.patch
patch -R /etc/nginx/nginx.conf < ~/nginx-change.patch
|
场景 2:源码级热修复
当远端服务器不能直接使用 Git,但需要应用代码修改时:
1 2 3 4 5 6 7
| cd /home/dev/project diff -u src/app.js.orig src/app.js > fix-login.patch
scp fix-login.patch user@prod-server:/tmp/ ssh user@prod-server "cd /var/www/app && patch -p1 < /tmp/fix-login.patch"
|
场景 3:目录级别批量管理
1 2 3 4 5 6 7 8 9 10
| diff -ruN project-v1/ project-v2/ --exclude=.git --exclude=node_modules > upgrade-v2.patch
wc -l upgrade-v2.patch head -50 upgrade-v2.patch
cd /opt/project patch -p1 < /tmp/upgrade-v2.patch
|
场景 4:反向补丁回滚
1 2 3 4 5
| patch -R -p1 < change.patch
patch -R --dry-run -p1 < change.patch
|
场景 5:仅查看差异的文件列表
1 2 3 4 5 6 7 8
| diff -rq config/ config-bak/ | grep -v "Only in"
diff -rq config/ config-bak/ | wc -l
diff -rq config/ config-bak/ | grep "Only in config/"
|
四、常见问题排查
| 问题 | 现象 | 解决方案 |
| 补丁失败:Hunk 不匹配 | Hunk #1 FAILED at 10. | 目标文件已被修改,与生成补丁时的版本不一致。重新生成补丁或手动合并。 |
| 路径不匹配 | can't find file to patch | 调整 -pN 参数级别。先用 patch --dry-run 测试不同 -p 值。 |
| 空白字符差异 | diff 输出大量无实质变化的差异 | 使用 -w 或 -b 忽略空白差异生成补丁。 |
| 二进制文件比较 | diff 输出 Binary files differ | 使用 diff -a 强制文本模式,或用 md5sum 校验。 |
| 大目录比较过慢 | diff -r 对大量文件执行缓慢 | 先用 diff -rq 列出差异文件,再对特定文件生成补丁。 |
| 换行符不一致 | Windows (CRLF) vs Linux (LF) | 使用 diff -w 忽略空白差异,或先用 dos2unix 统一换行符。 |
| 补丁部分应用 | 某些 Hunk 成功,某些失败 | 使用 --force 强制应用失败的 Hunk(谨慎!),或手动编辑补丁文件修正偏移量。 |
五、diff 和 patch 的进阶技巧
5.1 与 Git 的关系
Git 内部使用的差异算法就是 diff 的一种扩展,git diff 的输出就是 unified diff 格式:
1 2 3 4 5 6 7 8 9 10
| git diff src/app.js
git diff > my-changes.patch git format-patch HEAD~1
patch -p1 < my-changes.patch git am < 0001-fix-bug.patch
|
5.2 彩色差异化输出
1 2 3 4 5
| diff -u --color=always origin.conf modified.conf
colordiff -u origin.conf modified.conf
|
5.3 比较非文本文件
1 2 3 4 5 6 7
| diff -q doc1.pdf doc2.pdf
diffpdf doc1.pdf doc2.pdf
|
5.4 使用 diff3 三路合并
diff3 是 diff 的扩展,比较三个文件,常用于合并多个修改版本:
1
| diff3 -m my-file.txt base.txt their-file.txt > merged.txt
|
5.5 交互式补丁应用(sdiff)
sdiff 并排显示两个文件的差异:
1
| sdiff -w 120 nginx.conf.bak nginx.conf
|
六、最佳实践总结
- 始终用
-u 生成补丁 — unified 格式易读、可直接用于 patch
- 应用补丁前用
--dry-run — 先验证兼容性,避免破坏性操作
- 备份原始文件 — 使用
patch -b 自动生成 .orig 备份
- 使用
-p1 剥离一级路径 — 这是绝大多数开源项目的标准做法
- 用
diff -rq 快速检查目录差异 — 确认哪些文件有变更再深入分析
- 反转补丁用
-R — 快速回滚不需要重新生成反向补丁
- 排除无关目录 — 使用
--exclude 跳过 .git、node_modules、cache 等
参考命令速查
| 场景 | 命令 |
| 比较两个文件(统一格式) | diff -u file1 file2 |
| 递归比较两个目录 | diff -ruN dir1 dir2 |
| 仅列出差异文件名 | diff -rq dir1 dir2 |
| 忽略空白差异 | diff -uw file1 file2 |
| 生成补丁文件 | diff -u old new > patch.diff |
| 应用补丁 | patch -p1 < patch.diff |
| 试运行补丁 | patch --dry-run -p1 < patch.diff |
| 回滚补丁 | patch -R -p1 < patch.diff |
| 带备份应用补丁 | patch -b -p1 < patch.diff |
| 从文件读取补丁 | patch -i patch.diff |
| 彩色 diff 输出 | diff -u --color=always a b |
| 三路合并 | diff3 -m mine base theirs |
| 并排比较 | sdiff -w 120 file1 file2 |
本文由AI辅助生成,内容仅供参考