Linux patch 命令实战指南:用补丁文件为开源软件安装升级(linux-command 命令手册)
Linux patch 命令实战指南:用补丁文件为开源软件安装升级(linux-command 命令手册)
patch 是 Linux 系统中应用补丁文件(patch file / diff 输出)修改源代码的核心命令,也是内核与开源软件升级的标准方式。本指南基于 linux-command 仓库中的 patch 命令手册 展开,系统讲解其语法、全部选项与典型工作流,帮助你掌握"用
diff生成差异、用patch应用差异"的完整补丁流程,以及备份、路径剥离、模糊匹配、拒绝文件等进阶实战技巧。
一、patch 命令是什么
patch 命令 被用于为开放源代码软件安装补丁程序。它允许用户利用设置修补文件(patch file)的方式,修改、更新原始文件。如果一次仅修改一个文件,可以直接在命令行中下达指令依序执行;如果配合修补文件的方式,则能一次修补大批文件,这也是 Linux 系统内核升级的方法之一。
patch 与 diff 命令 是天然的一对:diff 负责"比较并输出差异",patch 负责"读取差异并应用修改"。从仓库的 命令分类索引 可以看出,patch 与 diff 同属"文件管理"分类,二者配合构成了完整的差异比较与补丁应用链路。
在 dist/data.json 的索引中,patch 条目被记录为:
"patch": {
"n": "patch",
"p": "/patch",
"d": "为开放源代码软件安装补丁程序"
}
说明该命令文档已经进入仓库的搜索索引,可通过项目生成的 Web 站点(npm run build 后将 command 目录的 Markdown 渲染为 HTML)直接检索查阅。
二、语法与参数
patch(选项)(参数)
参数
| 参数 | 说明 |
|---|---|
| 原文件 | 指定需要打补丁的原始文件 |
| 补丁文件 | 指定补丁文件 |
典型的调用形式为:
patch [选项] < 补丁文件 # 从标准输入读取补丁
patch [选项] -i 补丁文件 # 从指定补丁文件读取
patch [选项] -p1 < 补丁文件 # 剥离一层路径前缀后应用
三、选项完整解析
patch 的选项数量众多,本手册将其按功能分为五组,逐一说明。
1. 补丁格式解译(告诉 patch 差异文件是什么格式)
| 选项 | 说明 |
|---|---|
-c 或 --context |
把修补数据解译成关联性的差异(context 格式,即 diff -c 输出) |
-e 或 --ed |
把修补数据解译成 ed 指令可用的叙述文件(即 diff -e 输出) |
-n 或 --normal |
把修补数据解译成一般性的差异(normal 格式,即默认 diff 输出) |
-u 或 --unified |
把修补数据解译成一致化的差异(unified 格式,即 diff -u 输出) |
不同格式对应 diff 的不同输出模式。仓库中 diff 命令手册 提供了 unified 模式的输出示例,其格式为:
--- a.txt 2012-08-29 16:45:41.000000000 +0800
+++ b.txt 2012-08-29 16:45:51.000000000 +0800
@@ -1,7 +1,7 @@
a
a
a
-a
+b
a
a
a
以 - 开头的行表示删除,以 + 开头的行表示新增,@@ -l,n +l,n @@ 标明新旧文件中的行号范围。这是 Linux 内核与主流开源项目最常用的补丁格式。
2. 备份与版本控制(修改前先留后路)
| 选项 | 说明 |
|---|---|
-b 或 --backup |
备份每一个原始文件 |
-B<备份字首字符串> 或 --prefix=<备份字首字符串> |
设置文件备份时,附加在文件名称前面的字首字符串,该字符串可以是路径名称 |
-V<备份方式> 或 --version-control=<备份方式> |
用 -b 参数备份目标文件后,备份文件的字尾会被加上一个备份字符串;该字符串不仅可用 -z 参数变更,使用 -V 指定不同备份方式时,也会产生不同字尾的备份字符串 |
-Y<备份字首字符串> 或 --basename-prefix=<备份字首字符串> |
设置文件备份时,附加在文件基本名称开头的字首字符串 |
-z<备份字尾字符串> 或 --suffix=<备份字尾字符串> |
设置备份文件的字尾字符串。其效果与 -B 类似,差别在于:若修补作业使用的路径与文件名为 src/linux/fs/super.c,加上 backup/ 字符串后,文件 super.c 会备份于 src/linux/fs/backup 目录里 |
--backup-if-mismatch |
在修补数据不完全吻合,且没有刻意指定要备份文件时,才备份文件 |
--nobackup-if-mismatch |
在修补数据不完全吻合,且没有刻意指定要备份文件时,不要备份文件 |
典型用法:
# 应用补丁前备份所有原始文件(生成 *.orig 备份)
patch -b -i patch.diff
# 自定义备份后缀与备份目录
patch -b -z .bak -B /tmp/backup/ -i patch.diff
# 指定备份方式(numbered / existing / simple)
patch -b -V numbered -i patch.diff
-V 的三种备份方式对应不同的字尾策略:numbered 生成带编号的备份(如 .~1~),existing 仅在已有备份时编号,simple 使用 -z 指定的后缀(默认 ~)。--backup-if-mismatch 与 --nobackup-if-mismatch 则允许你按"补丁是否能精确吻合"这一条件来决定是否自动备份,兼顾安全性与磁盘占用。
3. 路径剥离(-p 层级,打补丁的关键)
| 选项 | 说明 |
|---|---|
-p<剥离层级> 或 --strip=<剥离层级> |
设置欲剥离几层路径名称 |
补丁文件中记录的文件路径往往来自作者机器的目录结构(例如 a/linux/fs/super.c、b/linux/fs/super.c)。-pN 告诉 patch 在匹配文件时剥离开头 N 个路径分量:
-p0:不剥离,按补丁文件中的完整路径查找文件;-p1:剥离第一层(最常见),即a/linux/fs/super.c对应实际文件linux/fs/super.c。
# 在源码树根目录下应用内核补丁
patch -p1 < patch.diff
这是开源软件补丁安装中最常使用的选项,务必根据补丁文件的路径前缀正确选择剥离层级。
4. 执行控制与容错(如何应对补丁不吻合)
| 选项 | 说明 |
|---|---|
-f 或 --force |
此参数的效果和指定 -t 参数类似,但会假设修补数据的版本为新版本 |
-F<监别列数> 或 --fuzz<监别列数> |
设置监别列数的最大值,允许 patch 在上下文行有少量偏差时仍尝试定位并应用 |
-g<控制数值> 或 --get=<控制数值> |
设置以 RCS 或 SCCS 控制修补作业 |
-N 或 --forward |
忽略修补数据较原始文件版本更旧,或该版本的修补数据已使用过的情况(即跳过已应用过的补丁) |
-R 或 --reverse |
假设修补数据是由新旧文件交换位置而产生(用于回滚补丁) |
-s 或 --quiet 或 --silent |
不显示指令执行过程,除非发生错误 |
-t 或 --batch |
自动略过错误,不询问任何问题 |
-T 或 --set-time |
此参数的效果和指定 -Z 参数类似,但以本地时间为主 |
-Z 或 --set-utc |
把修补过的文件更改、存取时间设为 UTC |
实战要点:
# 批量环境自动应用,跳过错误不询问
patch -t -p1 < patch.diff
# 只允许最多 3 行的上下文偏差(fuzz)
patch -F 3 -p1 < patch.diff
# 补丁已应用过?反向操作撤销修改(可用于回滚)
patch -R -p1 < patch.diff
-R 是运维中最实用的选项之一:当补丁已经被应用过、再次应用会失败时,使用 -R 可逆向撤销;-N 则相反,用于"确保不重复应用旧补丁"。-T/-Z 则用于让补丁后的文件时间戳保持一致,便于增量编译系统(如 make)正确判断文件是否需要重新编译。
5. 输入输出与差异处理
| 选项 | 说明 |
|---|---|
-d<工作目录> 或 --directory=<工作目录> |
设置工作目录,patch 在指定目录下执行 |
-D<标示符号> 或 --ifdef=<标示符号> |
用指定的符号(#ifdef 宏)把改变的地方标示出来 |
-E 或 --remove-empty-files |
若修补过后输出的文件其内容是一片空白,则移除该文件 |
-i<修补文件> 或 --input=<修补文件> |
读取指定的修补文件(代替从标准输入读取) |
-l 或 --ignore-whitespace |
忽略修补数据与输入数据中的跳格(tab)、空格字符差异 |
-o<输出文件> 或 --output=<输出文件> |
设置输出文件的名称,修补过的文件会以该名称存放,不直接覆盖原文件 |
-f<拒绝文件> 或 --reject-file=<拒绝文件> |
设置保存拒绝修补相关信息的文件名称,预设的文件名称为 .rej(GNU patch 的短选项为 -r) |
--binary |
以二进制模式读写数据,而不通过标准输出设备 |
-v 或 --version |
显示版本信息 |
--help |
在线帮助 |
--verbose |
详细显示指令的执行过程 |
# 从补丁文件读取而非标准输入
patch -i patch.diff
# 在指定目录下执行修补
patch -d /path/to/src -p1 < patch.diff
# 输出到新文件,不改动原文件(先预览效果)
patch -o newfile.txt < patch.diff
# 忽略空白差异(行尾空格、tab 不同也能匹配)
patch -l -p1 < patch.diff
其中 .rej 文件需要特别关注:当 patch 因上下文不吻合而无法应用某段修改时,这段差异会被写入 .rej 文件,主文件不被修改。此时需要人工检查 .rej 内容,决定是调整 fuzz 值、手动合并,还是重新生成补丁。
四、典型实战流程:从 diff 到 patch 的完整链路
1. 用 diff 生成统一格式补丁
diff -u old_version/file.c new_version/file.c > fix.patch
2. 应用补丁(含备份)
cd /path/to/source
patch -b -p1 < fix.patch
3. 验证与清理
# 静默模式执行,仅出错时输出
patch -s -p1 < fix.patch
# 检查是否有未应用成功的部分
ls -la *.rej
4. 回滚
patch -R -p1 < fix.patch
5. 条件编译式补丁
# 用 ifdef 宏包裹改动区域,便于多版本共存
patch -D MY_FEATURE -p1 < fix.patch
五、仓库中的索引与检索支撑
本仓库将每个命令文档组织为 command/<命令名>.md 的 Markdown 文件,并统一索引到 dist/data.json。patch 条目位于该文件的 1867 行附近,含名称(n)、页面路径(p)与简介(d)三个字段:
"patch": {
"n": "patch",
"p": "/patch",
"d": "为开放源代码软件安装补丁程序"
}
从 scripts/dash.mjs 的源码结构可以推断,构建 Dash/Zeal docset 时(npm run dash),会遍历 dist/data.json 中的全部索引,将每条命令的页面路径写入 SQLite 检索库 docSet.dsidx(scripts/dash.mjs)。也就是说,patch 这份文档既可通过 Web 版站点搜索,也可被 Dash/Zeal 等离线文档工具索引,方便随时查阅。
六、使用注意事项
- 先备份再修补:
patch直接修改源文件,正式环境务必配合-b/-B/-z做备份,或用-o输出到新文件先行预览。 - 路径层级要选对:
-p0与-p1选错是补丁应用失败最常见的原因,先阅读补丁文件头部确认路径前缀。 - 留意
.rej文件:拒绝的差异不会自动消失,需要人工处理,避免"补丁应用成功但修改缺失"的假象。 - 重复应用与回滚:已应用的补丁再次应用会失败,
-N可跳过旧补丁,-R可逆向撤销。 - 空白差异:不同编辑器的行尾处理差异会导致上下文不匹配,
-l与-F可在可控范围内提升容错。