diff
您可以通过控制 GNU输出中旧/新/未更改行的格式来实现此目的:
diff --new-line-format="" --unchanged-line-format="" file1 file2
应该对输入文件进行排序以使其正常工作。使用bash
(and zsh
) 您可以使用流程替换进行就地排序<( )
:
diff --new-line-format="" --unchanged-line-format="" <(sort file1) <(sort file2)
在上面的新行和未更改行被抑制,因此仅输出更改(即在您的情况下删除的行)。您还可以使用diff
其他解决方案不提供的一些选项,例如-i
忽略大小写或各种空格选项(-E
,-b
等-v
)以实现不太严格的匹配。
解释
options --new-line-format
,--old-line-format
并--unchanged-line-format
让您控制diff
格式差异的方式,类似于 printf
格式说明符。这些选项分别格式化新(添加)、旧(删除)和未更改的行。将一个设置为空 "" 可以防止输出那种行。
如果您熟悉统一差异格式,则可以使用以下方法部分重新创建它:
diff --old-line-format="-%L" --unchanged-line-format=" %L" \
--new-line-format="+%L" file1 file2
说明%L
符是有问题的行,我们在每个前面加上“+”“-”或“”,就像diff -u
(注意它只输出差异,它缺少每个分组更改顶部的---
+++
和行)。@@
您还可以使用它来做其他有用的事情,例如用.为每行编号%dn
。
该diff
方法(连同其他建议comm
和)仅产生带有排序join
输入的预期输出,但您可以使用它来进行就地排序。这是一个简单的(nawk)脚本(受 Konsolebox 答案中链接到的脚本的启发),它接受任意排序的输入文件,并按照它们在 file1 中出现的顺序输出缺失的行。<(sort ...)
awk
# output lines in file1 that are not in file2
BEGIN { FS="" } # preserve whitespace
(NR==FNR) { ll1[FNR]=$0; nl1=FNR; } # file1, index by lineno
(NR!=FNR) { ss2[$0]++; } # file2, index by string
END {
for (ll=1; ll<=nl1; ll++) if (!(ll1[ll] in ss2)) print ll1[ll]
}
这将 file1 的全部内容逐行存储在行号索引数组ll1[]
中,并将 file2 的全部内容逐行存储在行内容索引关联数组ss2[]
中。读取两个文件后,迭代ll1
并使用in
运算符确定 file1 中的行是否存在于 file2 中。diff
(如果有重复,这将对方法有不同的输出。)
如果文件足够大以至于存储它们都会导致内存问题,您可以通过仅存储 file1 并在读取 file2 的过程中删除匹配项来以 CPU 换取内存。
BEGIN { FS="" }
(NR==FNR) { # file1, index by lineno and string
ll1[FNR]=$0; ss1[$0]=FNR; nl1=FNR;
}
(NR!=FNR) { # file2
if ($0 in ss1) { delete ll1[ss1[$0]]; delete ss1[$0]; }
}
END {
for (ll=1; ll<=nl1; ll++) if (ll in ll1) print ll1[ll]
}
上面将 file1 的全部内容存储在两个数组中,一个由 line numberll1[]
索引,一个由 line content 索引ss1[]
。ll1[]
然后在读取 file2 时,从和中删除每个匹配的行ss1[]
。最后,输出 file1 中的剩余行,保留原始顺序。
在这种情况下,对于上述问题,您还可以使用 GNU分而治split
之(过滤是 GNU 扩展),使用 file1 的块重复运行并每次完全读取 file2:
split -l 20000 --filter='gawk -f linesnotin.awk - file2' < file1
注意命令行中-
含义stdin
的使用和位置。gawk
这是由split
file1 以每次调用 20000 行的块提供的。
对于非 GNU 系统上的用户,几乎可以肯定您可以获得一个 GNU coreutils 包,包括在 OSX 上作为提供 GNU的Apple Xcodediff
工具的一部分,awk
尽管只有 POSIX/BSDsplit
而不是 GNU 版本。