【问题标题】:Compare two files line by line and generate the difference in another file逐行比较两个文件并在另一个文件中生成差异
【发布时间】:2011-05-31 11:47:04
【问题描述】:

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

【问题讨论】:

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

标签: shell unix


【解决方案1】:

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 支持)。

【讨论】:

  • 记住两个文件必须排序并且是唯一的
  • 您可以将选项组合在一起:comm -23
  • “排序”是什么意思?这些行有相同的顺序吗?那么对于大多数用例来说它可能没问题 - 例如,通过与备份的旧版本进行比较来检查添加了哪些行。如果新添加的行不能在现有行之间,那就更成问题了。
  • @EgorHans: 如果文件有例如包含整数的行,例如 "3\n1\n3\n2\n" 行必须首先重新排序为升序或降序,例如"\1\n2\n3\n3\n" 相邻的重复项。那是“排序的”,两个文件必须以类似的方式排序。当较新的文件有新行时,它们是否“在现有行之间”无关紧要,因为在排序后它们不是,它们按排序顺序排列。
【解决方案2】:

Unix 实用程序 diff 正是为此目的而设计的。

$ diff -u file1 file2 > file3

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

【讨论】:

  • 那不做要求的工作;即使使用其他答案中建议的命令行开关,它也会插入一堆额外的字符。
【解决方案3】:

考虑一下:
文件 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 中的行 文件 2 中不存在。”

【讨论】:

  • 这个答案有两个限制:(1)它只适用于短行(默认情况下少于 80 个字符,尽管可以修改),更重要的是,(2)它添加一个每行末尾的“
  • 在许多情况下,您还需要使用-d,这将使diff尽最大努力找到可能的最小差异。 -i-E-w-B--suppress-blank-empty 偶尔也会有用,但并非总是如此。如果您不知道什么适合您的用例,请先尝试diff --help(当您不知道命令可以做什么时,这通常是个好主意)。
  • 另外,使用 --line-format=%L,你可以防止 diff 生成任何额外的字符(至少,帮助说它是这样工作的,但要尝试一下)。跨度>
  • 这也更短,似乎工作相同stackoverflow.com/a/27667185/1179925
【解决方案4】:

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

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

【讨论】:

  • 你应该为此获得一枚奖章!这正是我过去 2 小时一直在寻找的东西
  • 加入真的非常有用和快速。它可以用于许多情况,例如像这样查找差异,或者在两个文件中查找共同点。
【解决方案5】:

但是,没有grep 解决方案?

  • 仅存在于 file2 中的行:

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

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

    grep -Fxf file1 file2 > file3
    

【讨论】:

  • 完美!请注意,如果您的两个文件很大(15k+ 行),这些命令可能需要 10+ 秒。谢谢!
【解决方案6】:

试试

sdiff file1 file2

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

例如,

sdiff -w 185 file1.cfg file2.cfg

【讨论】:

  • 不错的实用工具!我喜欢它如何标记差异化的线条。使比较配置变得更加容易。这与排序一起是一个致命的组合(例如sdiff &lt;(sort file1) &lt;(sort file2)
【解决方案7】:

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

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

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

cat file1 | sd 'cat file2' > file3

在这个例子中可能没有太大的好处,但仍然考虑它;在某些情况下,您将无法使用 commgrep -Fdiff

这是一个blogpost我写的关于在终端上区分流,它引入了sd。

【讨论】:

    【解决方案8】:

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

    我认为获得不同的行的最佳方式(没有额外的字符,没有重新排序)是diffgrepawk(或类似)的组合。

    如果行不包含任何“

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

    但这会从行中删除“

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

    这个单行比较两个文件,然后过滤掉 diff 的 ed 样式输出,然后删除 diff 添加的尾随“

    【讨论】:

    • comm 不需要排序(在较新的版本中?) - 只需使用 --nocheck-order。从 CLI 操作 csvs 时,我经常使用它
    【解决方案9】:

    您可以使用具有以下输出格式的diff

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

    --old-line-format='',如果文件 2 中的行不同,则禁用文件 1 的输出。
    --unchanged-line-format='',如果行相同,则禁用输出。

    【讨论】:

      【解决方案10】:

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

      diff -y file1 file2 > file3
      

      而在file3(不同的行中间有一个符号|):

      same     same
      diff_1 | diff_2
      

      【讨论】:

        【解决方案11】:

        使用 Diff 实用程序并仅提取输出中以

        【讨论】:

          【解决方案12】:
          diff a1.txt a2.txt | grep '> ' | sed 's/> //' > a3.txt
          

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

          【讨论】:

          • 这是个坏主意。您还需要修改以&lt; 开头的行。如果您交换输入文件的顺序,您将看到这一点。即使您这样做了,您也希望通过使用更多 sed 来省略 grep:` diff a1 a2 | sed '/> /s///'` 这仍然可以在正确的情况下中断包含&gt;&lt; 的行,并且仍然 留下额外的行号描述行。如果您想尝试这种方法,更好的方法是:diff -C0 a1 a2 | sed -ne '/^[+-] /s/^..//p'
          【解决方案13】:

          如果您有一个包含单列甚至多列的 CSV 文件,您可以使用 sqlite3 嵌入式数据库逐行执行这些“差异”操作。它带有 python,所以应该可以在大多数 linux/macs 上使用。您可以在 bash shell 上编写 sqlite3 命令脚本,而无需编写 python。

          1. 创建您的 a.csv 和 b.csv 文件
          2. 确保使用命令“sqlite3 -help”安装 sqlite3
          3. 直接在 Linux/Mac shell 上运行以下命令(或将其放入脚本中)
          echo "
          .mode csv
          .import a.csv atable
          .import b.csv btable
          create table result as select * from atable EXCEPT select * from btable;
          .output result.csv
          select * from result ;
          .quit
          " | sqlite3 temp.db
          

          注意:确保每个 sqlite3 命令都有一个换行符

          它是如何工作的

          1. 将 2 个 csv 分别导入“atable”和“btable”。
          2. 使用“except”sql 运算符选择“atable”中可用但“btable”中缺少的数据。使用 select 查询语句创建“结果”表
          3. 通过运行“select * from result;”将结果表输出到result.csv

          如果你需要对特定的列进行操作,sqlite3 或任何 db 都是可行的。

          我尝试使用内置的 diff 和 comm 工具对多个 GB 文件进行比较。 Sqlite 比 linux 实用程序领先一英里。

          【讨论】:

            猜你喜欢
            • 2012-02-18
            • 1970-01-01
            • 2012-10-28
            • 2014-06-08
            • 1970-01-01
            • 2022-10-17
            • 2011-12-07
            • 2015-08-10
            • 2021-08-30
            相关资源
            最近更新 更多