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

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

421449 次浏览

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

$ diff -u file1 file2 > file3

查看手册和互联网的选项,不同的输出格式等。

使用 Diff 实用程序,只提取输出中以 < 开头的行

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

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

想想这个:
文件 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

这将回答你的问题:

”... 其中包含文件1中的行 没有出现在文件2中。”

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

试试看

sdiff file1 file2

在大多数情况下,这种方法对我来说效果要好得多。 如果行的顺序不重要(例如某些文本配置文件) ,您可能希望先对文件进行排序。

比如说,

sdiff -w 185 file1.cfg file2.cfg

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

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

如果行中不包含任何“ <”,则可以使用简短的一行程序:

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

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

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

这个一行程序区分两个文件,然后过滤掉 diff 的 ed 样式输出,然后删除 diff 添加的尾部“ <”。即使行本身包含一些“ <”,这也是可行的。

如果你需要用核心组件来解决这个问题,公认的答案是好的:

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

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

cat file1 | sd 'cat file2' > file3

在这个例子中可能没有那么多好处,但是仍然要考虑它; 在某些情况下,您将不能使用 commgrep -Fdiff

下面是我写的关于终端上不同流的 博客文章,它引入了 sd。

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

在这个帖子里,我试了几乎所有的答案,但没有一个是完整的。经过几次尝试后,我终于成功了。差异会给你一些不同,但与一些不必要的特殊字符。其中实际的差异行以’>’开头。因此,下一步是以“ >”开头的 Grep行,然后用 Sed删除相同的行。

然而,没有 grep解决方案?

  • 只存在于文件2中的行:

      grep -Fxvf file1 file2 > file3
    
  • 只存在于文件1中的行:

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

      grep -Fxf file1 file2 > file3
    

开关说明(另见 man grep) :

  • -F告诉 grep 将 PATTERNS 解释为固定字符串,而不是正则表达式。
  • -x告诉 grep 只选择那些完全匹配整行而非部分匹配的匹配。
  • 使用 -f,grep 从 FILE 中获取模式,每行一个。
  • -v只是反转匹配的感觉,选择不匹配的行。

您可以使用 diff进行以下输出格式设置:

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

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

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

diff -y file1 file2 > file3

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

same     same
diff_1 | diff_2

如果您有一个带有 单列甚至多列的 CSV 文件,那么您可以使用 sqlite3嵌入式 db 逐行执行这些“ diff”操作。它附带了 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 分别导入到“ table”和“ btable”中。
  2. 使用“ 除了”sql 操作符来选择在“ table”中可用但在“ btable”中缺少的数据。使用 select 查询语句创建一个“ result”表
  3. 通过运行“ select * from result”将结果表输出到 result. csv;

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

我尝试过使用内置的 diff 和 comm 工具对多个 GB 文件进行 diff。Sqlite 远远超过 Linux 实用程序。

linecount=0
while IFS= read -r line1; do
let linecount=linecount+1
IFS= read -r line2  < $2


if [ "$line1" != "$line2" ] ; then
echo "============diff: $linecount"
echo "LINE1 $line1";
echo "LINE2 $line2";
echo ""
fi
done < $1