逐行比较两个文件,并在另一个文件中产生差异


120

我想将file1与file2进行比较,并生成一个file3,其中包含file1中不存在于file2中的行。


我尝试了diff,但是它会在不同行的前面生成一些数字和其他符号,这使我很难比较文件。
太阳

Answers:


215

diff(1)不是答案,而comm(1)是答案。

NAME
       comm - compare two sorted files line by line

SYNOPSIS
       comm [OPTION]... FILE1 FILE2

...

       -1     suppress lines unique to FILE1

       -2     suppress lines unique to FILE2

       -3     suppress lines that appear in both files

所以

comm -2 -3 file1 file2 > file3

输入文件必须排序。如果不是,请先对其进行排序。这可以通过一个临时文件来完成,或者...

comm -2 -3 <(sort file1) <(sort file2) > file3

只要您的Shell支持进程替换(bash可以)。


1
请记住,两个文件必须排序并且是唯一的
andy

6
您可以将选项分组在一起:comm -23
Paolo M

“排序”是什么意思?线条顺序相同吗?然后,对于大多数用例来说可能就很好了-例如,通过与备份的较旧版本进行比较来检查添加了哪些行。如果新添加的行不能在现有行之间,那就更成问题了。
Egor Hans

@EgorHans:如果文件包含例如包含整数的行,例如“ 3 \ n1 \ n3 \ n2 \ n”,则必须首先以升序或降序对行进行重新排序,例如“ \ 1 \ n2 \ n3 \ n3 \ n”重复邻。那是“排序的”,并且两个文件必须以类似的方式排序。当较新的文件包含新行时,它们是否“在现有行之间”并不重要,因为在排序之后,它们不是按排序顺序排列的。
sorpigal

47

Unix实用程序diff正是用于此目的。

$ diff -u file1 file2 > file3

有关选项,不同的输出格式等,请参见手册和Internet。


7
那没有完成要求的工作;即使使用其他答案中建议的命令行开关,它也会插入很多额外的字符。
Xenocyon

20

考虑一下:
a.txt文件:

abcd
efgh

文件b.txt:

abcd

您可以通过以下方式找到区别:

diff -a --suppress-common-lines -y a.txt b.txt

输出将是:

efgh 

您可以使用以下方法在输出文件(c.txt)中重新分配输出:

diff -a --suppress-common-lines -y a.txt b.txt > c.txt

这将回答您的问题:

“ ...包含file1中不存在于file2中的行。”


2
此答案有两个局限性:(1)它仅适用于短行(默认情况下少于80个字符,尽管可以修改),更重要的是,(2)在每个行的末尾添加“ <”必须用另一个程序(例如awk,sed)删除的行。
sergut 2015年

在许多情况下,您还需要使用-d,它将尽diff最大努力找到可能的最小差异。-i-E-w-B并且--suppress-blank-empty也可能是有用的偶然,但并非总是如此。如果您不知道什么适合您的用例,请diff --help首先尝试(当您不知道命令可以做什么时,通常是个好主意)。
Egor Hans,

同样,使用--line-format =%L,可以避免diff生成任何多余的字符(至少,帮助说它可以像这样工作,但仍然可以尝试)。
Egor Hans,

而且这更短,似乎可以使用相同的stackoverflow.com/a/27667185/1179925
mrgloom,

8

有时diff是您需要的实用程序,但有时join更合适。这些文件需要预先排序,或者,如果您使用支持进程替换的外壳程序(例如bash,ksh或zsh),则可以即时进行排序。

join -v 1 <(sort file1) <(sort file2)

您应该为此获得一枚奖牌!这正是我过去两个小时一直在寻找的东西
Zatarra

7

尝试

sdiff file1 file2

通常,在大多数情况下,它对我来说要好得多。如果行的顺序不重要,则可能需要对文件进行排序(例如,一些文本配置文件)。

例如,

sdiff -w 185 file1.cfg file2.cfg

1
不错的工具!我喜欢它如何区分差异线。使比较配置更加容易。这与排序是致命的组合(例如sdiff <(sort file1) <(sort file2)
jmagnusson

3

如果您需要使用coreutils解决此问题,那么可以接受的答案是好的:

comm -23 <(sort file1) <(sort file2) > file3

您还可以使用sd(流差异),它不需要排序也不需要进程替换,并且支持无限流,如下所示:

cat file1 | sd 'cat file2' > file3

在这个例子中,可能没有太大的好处,但还是考虑一下;在某些情况下,您将无法使用commnor grep -F或nor diff

这是我写的关于在终端上扩散流的博客文章,其中介绍了sd。


3

但是,没有grep解决办法吗?

  • 仅在file2中存在的行:

    grep -Fxvf file1 file2 > file3
  • 仅在file1中存在的行:

    grep -Fxvf file2 file1 > file3
  • 这两个文件中都存在的行:

    grep -Fxf file1 file2 > file3

2

已经有很多答案,但是没有一个完美的恕我直言。Thanatos的答案在每行中留下了一些额外的字符,Sorpigal的答案要求对文件进行排序或预排序,这可能并不适合所有情况。

我觉得越来越是不同的,并没有别的(没有多余的字符,没有重新排序)行的最好方式是组合diffgrepawk(或类似)。

如果这些行不包含任何“ <”,则简短的单行代码可以是:

diff urls.txt* | grep "<" | sed 's/< //g'

但这会从行中删除每个“ <”(小于空格)的实例,这并不总是可以的(例如源代码)。最安全的选择是使用awk:

diff urls.txt* | grep "<" | awk '{for (i=2; i<NF; i++) printf $i " "; print $NF}'

这个单线diff比较两个文件,然后过滤掉diff的ed样式输出,然后删除diff添加的尾随“ <”。即使这些行本身包含一些“ <”也可以使用。


1
comm不需要排序(在新版本中?)-只需使用--nocheck-order。从CLI操纵csv时,我经常使用它
ak5

2

我提到惊讶没人diff -y产生并排侧输出,例如:

diff -y file1 file2 > file3

file3(不同的线|中间有一个符号):

same     same
diff_1 | diff_2


0
diff a1.txt a2.txt | grep '> ' | sed 's/> //' > a3.txt

我尝试了该线程中几乎所有的答案,但没有一个是完整的。经过几步之后,一条路为我工作。diff会给您带来不同,但会有一些不需要的特殊字符。您实际的差异线以“>”开头。所以下一步是grep行以'>'开头,然后用sed删除它们。


1
这是一个坏主意。您还需要修改以开头的行<。如果交换输入文件的顺序,则会看到此信息。即使您执行了此操作,也希望grep通过使用更多sed 来忽略它:`diff a1 a2 | sed'/> / s ///'`仍然可以中断包含><在正确情况下的行,并仍然留下多余的行来描述行号。如果您想尝试这种方法,则更好的方法是:diff -C0 a1 a2 | sed -ne '/^[+-] /s/^..//p'
sorpigal

0

您可以使用diff以下输出格式:

diff --old-line-format='' --unchanged-line-format='' file1 file2

--old-line-format='',如果file2中的行不同,则禁用file1的输出。
--unchanged-line-format='',如果行相同则禁用输出。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.