如何在 Linux 终端中比较两个文本文件

想知道两个文本文件版本之间的不同之处吗?diff 命令就是为此而生的。本教程将以简单易懂的方式展示如何在 Linux 和 macOS 系统中使用 diff 命令。

理解 diff 的工作原理

diff 命令用于比较两个文件,并生成一个详细的差异列表。更具体地说,它会生成一份清单,列出需要对第一个文件进行的修改,以便使其与第二个文件完全一致。理解这一点有助于你更好地解读 diff 的输出结果。diff 的主要目标是找出源代码文件间的差异,并生成可供其他程序(如 patch 命令)读取和操作的输出。本教程重点介绍 diff 命令最常用且对人友好的用法。

让我们深入研究两个文件。在命令行中,文件的排列顺序至关重要,它决定了 diff 将哪个文件视为“第一个文件”,哪个文件视为“第二个文件”。在以下示例中,alpha1 是第一个文件,而 alpha2 是第二个文件。这两个文件都包含了北约音标字母,但 alpha2 文件经过了进一步的编辑,因此两者并不完全相同。

我们可以使用以下命令来比较这两个文件:在命令行输入 diff,然后输入一个空格,接着输入第一个文件的名称,再输入一个空格,最后输入第二个文件的名称,然后按 Enter 键。

diff alpha1 alpha2

如何分析 diff 的输出呢?一旦掌握了其中的规律,你会发现它并不复杂。每一个差异都会按照顺序单独列出,并且都有相应的标记。标记包含字母两侧的数字,例如 4c4。第一个数字代表 alpha1 中的行号,第二个数字代表 alpha2 中的行号。中间的字母可能是:

  • c:表示需要更改第一个文件中的行以匹配第二个文件中的行。
  • d:表示必须从第一个文件中删除行才能匹配第二个文件。
  • a:表示必须在第一个文件中添加内容才能使其与第二个文件匹配。

例如,4c4 表示必须修改 alpha1 中的第 4 行,使其与 alpha2 的第 4 行一致。这是 diff 在这两个文件中发现的第一个差异。

< 开头的行引用的是第一个文件 alpha1,而以 > 开头的行则引用第二个文件 alpha2> Dave 这行告诉我们 alpha2 中第 4 行的内容是 Dave。总而言之,我们需要将 alpha1 的第 4 行中的 Delta 替换为 Dave,以使两文件中的该行匹配。

下一个变化由 12c12 指示。 按照相同的逻辑,这意味着 alpha1 的第 12 行是 Lima,而 alpha2 的第 12 行是 Linux

第三个变化表示从 alpha2 中删除了一行。标签 21d20 可以解读为“需要删除第一个文件的第 21 行,以便两个文件从第 20 行开始同步”。

第四个差异标记为 26a26,28。此更改涉及在 alpha2 中添加的三行内容。请注意标签中的 26,28。用逗号分隔的两个行号表示一个行号范围。在这个例子中,范围是从第 26 行到第 28 行。此标签的含义是“在第一个文件的第 26 行之后,从第二个文件添加第 26 到 28 行”。我们可以看到需要在 alpha1 中添加 alpha2 中的三行。这些行包含 QuirkStrangeCharm 等单词。

简明扼要的单行输出

如果您只想知道两个文件是否相同,可以使用 -s (报告相同文件)选项。

diff -s alpha1 alpha3

您可以使用 -q (简要)选项来获得关于两个文件是否不同的简要声明。

diff -q alpha1 alpha2

需要注意的是,对于两个相同的文件,-q 选项不会输出任何信息。

并排查看差异

-y (并排)选项使用不同的布局来展示文件差异。在并排视图中使用 -W (宽度)选项通常很有用,它可以限制显示的列数,避免输出因换行而难以阅读。以下命令告诉 diff 生成并排显示,并将输出限制为 70 列。

diff -y -W 70 alpha1 alpha2

命令行中的第一个文件 alpha1 显示在左侧,第二个文件 alpha2 显示在右侧。每个文件的行都并排显示。在 alpha2 中的行旁边,会有指示符字符来表明该行被修改、删除或添加。

  • |:表示第二个文件中已修改的行。
  • <:表示从第二个文件中删除的行。
  • >:表示添加到第二个文件中但在第一个文件中不存在的行。

如果希望得到更简洁的并排差异摘要,可以使用 --suppress-common-lines 选项。这将强制 diff 只列出更改、添加或删除的行。

diff -y -W 70 --suppress-common-lines alpha1 alpha2

为输出添加颜色

另一个名为 colordiff 的实用工具可以将颜色突出显示添加到 diff 的输出中。这使得查看哪些行有差异变得更加容易。

如果您使用的是 Ubuntu 或其他基于 Debian 的发行版,可以使用 apt-get 将此软件包安装到您的系统中。在其他 Linux 发行版上,请使用您 Linux 发行版的包管理工具进行安装。

sudo apt-get install colordiff

使用 colordiff 的方式与使用 diff 相同。

实际上,colordiffdiff 的一个包装器,diff 在幕后完成所有工作。因此,所有 diff 选项都适用于 colordiff

提供上下文信息

为了在屏幕上显示所有行和仅显示已更改行之间找到一个折衷方案,我们可以要求 diff 提供一些上下文。有两种方法可以做到这一点。两种方法都达到了相同的目的,即在每条更改的行之前和之后显示一些行。您将能够看到文件中差异发生的位置的上下文。

第一种方法使用 -c (复制上下文)选项。

colordiff -c alpha1 alpha2

diff 输出有一个标题。标题列出了两个文件名及其修改时间。在第一个文件名前面有一个星号 (*),在第二个文件名前面有一个破折号 (-)。星号和破折号用于指示输出中的行属于哪个文件。

中间带有 1,7 的星号行表示我们正在查看来自 alpha1 的行。准确地说,我们正在查看第 1 到第 7 行。Delta 一词被标记为已更改。它旁边有一个感叹号(!),它是红色的。该行前后会显示三行未更改的文本,以便我们可以看到文件中该行的上下文。

中间带有 1,7 的破折号行表示我们正在查看来自 alpha2 的行。同样,我们正在查看第 1 到第 7 行,第 4 行中的单词 Dave 被标记为不同。

colordiff -C 2 alpha1 alpha2

每个更改的行上方和下方的三行上下文是默认值。您可以指定 diff 提供多少行上下文。为此,请使用带有大写“C”的 -C (复制上下文)选项,并提供您想要的行数:

colordiff -u alpha1 alpha2

提供上下文的第二个 diff 选项是 -u (统一上下文)选项。

和以前一样,我们在输出中有一个标题。这两个文件被命名,并显示了它们的修改时间。 alpha1 的名称前有一个破折号(-),而 alpha2 的名称前有一个加号(+)。这告诉我们破折号将用于指代 alpha1,而加号将用于指代 alpha2。 整个列表中散布着以符号 @ 开头的行。这些行标记着每个差异的开始。它们还告诉我们每个文件中显示了哪些行。

我们显示了标记为不同的行之前和之后的三行,以便我们可以看到更改行的上下文。在统一视图中,有差异的行在另一行之上显示。来自 alpha1 的行前面有一个破折号,来自 alpha2 的行前面有一个加号。这个显示在 8 行中完成了上述复制上下文显示需要 15 行才能完成的工作。

colordiff -U 2 alpha1 alpha2

正如您所料,我们可以要求 diff 精确地提供我们希望看到的统一上下文行数。为此,请使用带有大写“U”的 -U (统一上下文)选项,并提供您想要的行数:

忽略空格和大小写

colordiff -y -W 70 test4 test5

让我们分析另外两个文件,test4test5。 它们包含六个超级英雄的名字。

结果显示,diff 发现黑寡妇、蜘蛛侠和雷神的行没有差异。它确实标记了美国队长、钢铁侠和绿巨人的行有变化。

那么,差异到底是什么呢? 在 test5 中,Hulk 的拼写是小写的 h,而美国队长在 CaptainAmerica 之间有一个额外的空格。很明显,但是 Ironman 行有什么问题?没有明显的差异。这是一个好的经验法则:如果您看不出差异,那很可能就是空格。该行的末尾几乎可以肯定有一个或两个多余的空格或制表符。

如果这些差异对您而言并不重要,您可以指示 diff 忽略特定类型的行差异,包括:

  • -i:忽略大小写的差异。
  • -Z:忽略尾随空格。
  • -b:忽略空格数量的变化。
  • -w:忽略所有空格的变化。
colordiff -i -y -W 70 test4 test5

让我们让 diff 再次检查这两个文件,但这次忽略大小写的任何差异。

colordiff -i -Z -y -W 70 test4 test5

包含 The HulkThe hulk 的行现在被认为是匹配的,小写的 h 没有区别。让我们要求 diff 也忽略尾随空格。

colordiff -i -w -y -W 70 test4 test5

正如预期的那样,尾随空格一定是 Ironman 行的差异,因为 diff 不再标记该行有差异。剩下的就是美国队长。让我们让 diff 忽略大小写,并忽略所有空格问题。

通过告诉 diff 忽略我们不关心的差异,diff 告诉我们,就我们的目的而言,文件是匹配的。diff 命令还有更多选项,但其中大多数与生成机器可读的输出有关。您可以在 Linux 上查看 手册页了解更多信息。

我们在上面的示例中使用的选项将使您能够使用命令行和肉眼来跟踪文本文件版本之间的所有差异。