如何在 Linux 中将补丁应用于文件(并创建补丁)

Linux 的 patch 命令允许用户便捷且安全地将修改从一个文件集合应用到另一个文件集合。本文将深入探讨如何简单有效地使用该命令。

patchdiff 命令的协同作用

假设您的计算机上存在一个文本文件,并且您收到了该文件的修订版本。您应该如何快速地将修订版本中的更改转移到原始文件呢?这就是 patchdiff 命令发挥作用的地方。这两个命令在 Linux 和 macOS 等类 Unix 操作系统中均可使用。

diff 命令用于比较同一文件的两个不同版本,并列出它们之间的差异。这些差异可以被存储在一个称为补丁文件的特殊文件中。

patch 命令则可以读取该补丁文件,并将文件内容视为一系列指令。它会按照这些指令将修改过的文件中的更改复制到原始文件

现在,想象一下如果这个过程发生在一个完整的文本文件目录中,一次性完成所有操作,这就是 patch 命令的强大之处。

有时,您可能不会收到修订后的文件,而是只收到补丁文件。当您能够通过发送或发布一个文件来简化下载过程时,为什么要发送数十个文件呢?

那么,如何利用补丁文件来实际修补您的文件呢? 这确实是一个值得探讨的问题,接下来我们将详细解释。

patch 命令最常用于处理软件源代码文件的开发人员,但它同样适用于任何文本文件集,无论其用途如何,是否是源代码都无关紧要。

我们的示例场景

在本例中,我们假设当前工作目录名为 “work”,其中包含两个子目录: “working” 和 “latest”。 “working” 目录包含一组源代码文件,而 “latest” 目录则包含这些源代码文件的最新版本,其中部分文件已被修改。

为了安全起见,”working” 目录是文本文件当前版本的一个副本,而不是唯一副本。

查找文件两个版本之间的差异

diff 命令用于查找两个文件之间的差异。它的默认行为是在终端窗口中列出修改过的行。

假设我们有一个名为 “slang.c” 的文件。我们会将 “working” 目录中的版本与 “latest” 目录中的版本进行比较。

-u(统一)选项会告诉 diff 命令同时列出每个更改部分之前和之后的一些未修改的文本行。 这些行被称为上下文行,它们帮助 patch 命令精确定位原始文件中需要进行更改的位置。

我们需要提供文件的名称,以便 diff 命令知道要比较哪些文件。首先列出原始文件,然后列出修改后的文件。以下是我们执行 diff 命令的示例:

diff -u working/slang.c latest/slang.c

diff 命令会生成一个输出列表,显示文件之间的差异。如果文件完全相同,则不会有任何输出。这种类型的输出来自 diff, 可以确认两个文件版本之间存在差异,并且原始文件需要进行修补。

生成补丁文件

要将这些差异捕获到补丁文件中,请使用以下命令。它与上面的命令相同,只是将 diff 命令的输出重定向到一个名为 “slang.patch” 的文件中。

diff -u working/slang.c latest/slang.c > slang.patch

补丁文件的名称可以自定义,您可以根据自己的喜好命名。但建议使用 “.patch” 作为文件扩展名,因为它能清晰地表明该文件的类型。

要使 patch 命令根据补丁文件修改 “working/slang.c” 文件,请使用以下命令。 -u(统一)选项告诉 patch 命令,该补丁文件包含统一的上下文行。 换句话说,因为我们使用带有 -u 选项的 diff 命令,所以我们也将使用带有 -u 选项的 patch 命令。

patch -u working/slang.c -i slang.patch

如果一切顺利,将有一行输出显示 patch 命令正在修补该文件。

备份原始文件

我们可以使用 -b(备份)选项指示 patch 命令在应用补丁之前创建原始文件的备份副本。-i(输入)选项用于指定要使用的补丁文件的名称。

patch -u -b working/slang.c -i slang.patch

该文件会像之前一样被修补,输出中没有明显差异。但是,如果您查看 “working” 文件夹,会发现已创建了一个名为 “slang.c.orig” 的文件。该文件的日期和时间戳表明 “slang.c.orig” 是原始文件,而 “slang.c” 是由补丁命令创建的新文件。

diff 命令与目录

我们可以使用 diff 命令来创建一个补丁文件,其中包含了两个目录中所有文件之间的差异。然后,我们可以将该补丁文件与 patch 命令一起使用,通过单个命令将这些差异应用到 “working” 文件夹中的文件。

我们将与 diff 命令一起使用的选项包括:之前使用过的 -u(统一上下文)选项、用于让 diff 命令查看任何子目录的 -r(递归)选项以及 -N(新文件)选项。

-N 选项告诉 diff 命令如何处理 “latest” 目录中存在但 “working” 目录中不存在的文件。 它强制 diff 命令将指令放入补丁文件中,以便 patch 命令创建存在于 “latest” 目录中但 “working” 目录中缺失的文件。

您可以将这些选项组合在一起,使用单个连字符 (-)。

请注意,我们只提供了目录名称,而没有指定 diff 命令要查看的特定文件。

diff -ruN working/ latest/ > slang.patch

深入了解补丁文件

让我们快速浏览一下补丁文件。 我们将使用 less 命令来查看它的内容。

该文件的顶部显示了 “slang.c” 文件的两个版本之间的差异。

继续向下滚动补丁文件,我们看到它描述了另一个名为 “structs.h” 的文件中的更改。 这验证了补丁文件确实包含多个文件不同版本之间的差异。

三思而后行

修补大量文件可能会让人感到不安,因此我们将使用 --dry-run 选项来检查一切是否正常,然后再采取行动并提交更改。

--dry-run 选项告诉 patch 命令执行除了实际修改文件之外的所有操作。 patch 命令将对文件执行所有预检,并且如果遇到任何问题,它会报告这些问题。无论如何,不会修改任何文件。

如果没有报告任何问题,我们可以重复该命令,但不使用 --dry-run 选项,从而自信地修补我们的文件。

-d(目录)选项告诉 patch 命令在哪个目录中工作。

请注意,我们没有使用 -i(输入)选项来告诉 patch 命令哪个补丁文件包含来自 diff 命令的指令。 相反,我们将补丁文件重定向到 patch 命令中。

patch --dry-run -ruN -d working  < slang.patch

在整个目录中, diff 命令发现有两个文件需要修补。 patch 命令已经检查了有关这两个文件的修改说明,并且没有报告任何问题。

预检检查正常,我们准备开始。

修补目录

要真正将补丁应用到文件,我们使用之前的命令,但不使用 --dry-run 选项。

patch -ruN -d working  < slang.patch

这一次,每行输出不再以 “checking” 开头,而是以 “patching” 开头。

而且没有报告任何问题。 我们可以编译源代码,从而确保我们使用的是最新版本的软件。

解决差异

这是迄今为止使用 patch 命令最简单和最安全的方法。将目标文件复制到一个文件夹中,并修补该文件夹。当您确信修补过程已无错误地完成时,再将它们复制回去。