![对文件中的特定列使用 Diff](https://linux22.com/image/60290/%E5%AF%B9%E6%96%87%E4%BB%B6%E4%B8%AD%E7%9A%84%E7%89%B9%E5%AE%9A%E5%88%97%E4%BD%BF%E7%94%A8%20Diff.png)
是否可以对文件中的特定列使用 diff?
文件1
Something 123 item1
Something 456 item2
Something 768 item3
Something 353 item4
文件2
Another 123 stuff1
Another 193 stuff2
Another 783 stuff3
Another 353 stuff4
输出(预期)
Something 456 item2
Something 768 item3
Another 193 stuff2
Another 783 stuff3
我想要diff
每个文件的第二列,然后,结果将包含差异列,但包含整行。
答案1
awk
是比较文件列的更好工具。例如,请参阅以下问题的答案:比较不同文件的两列,如果匹配则打印-- 对于打印匹配列的行有类似的答案。
因为你想打印行不匹配,我们可以创建一个awk
命令来打印 file2 中第 2 列包含的行不是在file1中看到:
$ awk 'NR==FNR{c[$2]++;next};c[$2] == 0' file1 file2
Another 193 stuff2
Another 783 stuff3
正如 terdon 中类似的解释上述问题,
NR==FNR
:NR 是当前输入行号,FNR 是当前文件的行号。仅当读取第一个文件时,两者才会相等。c[$2]++; next
:如果这是第一个文件,则将第二个字段保存在c
数组中。然后,跳到下一行,以便仅应用于第一个文件。c[$2] == 0
:只有当这是第二个文件时,else 块才会被执行,因此我们检查该文件的字段 2 是否已经被看到 (c[$2]==0
),如果已经被看到,我们打印该行。在 中awk
,默认操作是打印该行,因此如果c[$2]==0
为 true,则将打印该行。
但您还需要 file1 中第 2 列与 file2 中不匹配的行。您可以通过在同一命令中简单地交换它们的位置来获得这一点:
$ awk 'NR==FNR{c[$2]++;next};c[$2] == 0' file2 file1
Something 456 item2
Something 768 item3
所以现在你可以通过使用两次来生成你想要的输出awk
。也许具有更多awk
专业知识的人可以一次性完成它。
您用 标记了您的问题/ksh
,所以我假设您正在使用 korn shell。您ksh
可以为 diff 定义一个函数,例如diffcol2
,以使您的工作更轻松:
diffcol2()
{
awk 'NR==FNR{c[$2]++;next};c[$2] == 0' $2 $1
awk 'NR==FNR{c[$2]++;next};c[$2] == 0' $1 $2
}
这具有您想要的行为:
$ diffcol2 file1 file2
Something 456 item2
Something 768 item3
Another 193 stuff2
Another 783 stuff3
答案2
我不认为 diff (即使与 cut 结合使用)足够灵活来处理这个问题。看起来你真正想要的是 file1 中不在 file2 中的键,反之亦然 - 不是严格意义上的逐行差异。如果输入文件很大,我会使用 perl,但对于小文件,此 awk 脚本适用于提供的输入:
%cat a.awk
BEGIN {
while (getline < "file1") {
line=$0;
split(line,f," ");
key=f[2];
f1[key]=line
}
while (getline < "file2") {
line=$0;
split(line,f," ");
key=f[2];
f2[key]=line
}
}
END {
for (c in f1) {
if (c in f2 == 0) print f1[c]
}
for (c in f2) {
if (c in f1 == 0) print f2[c]
}
}
这就是运行它的方式(注意 /dev/null 的技巧,因为 awk 需要一个输入文件作为参数:
%awk -f a.awk /dev/null
Something 456 item2
Something 768 item3
Another 193 stuff2
Another 783 stuff3
答案3
像这样的事情一次性就可以解决问题:
$ awk '{t[$2]=t[$2]$0"\n";NR==FNR?a[$2]=1:b[$2]=1}END{for(i in t)if(a[i]!=b[i])printf"%s",t[i]}' file1 file2
Another 193 stuff2
Something 456 item2
Something 768 item3
Another 783 stuff3
解释:
- 对于每行:
{t[$2]=t[$2]$0"\n";
t
连接由该键索引的表中具有相同键的所有行,NR==FNR?a[$2]=1:b[$2]=1}
如果键在第一个文件中,则将其放入a
表中,否则放入b
表中;
END
在最后:{for(i in t)
对于表中的每个键t
:if(a[i]!=b[i])
如果密钥仅存在于一个文件中:printf"%s",t[i]}
使用该键打印所有行。
如果键重复(打印另一个文件中没有键的所有行),这也适用,但行的顺序不会保留(行按键的字典顺序打印)。