对文件中的特定列使用 Diff

对文件中的特定列使用 Diff

是否可以对文件中的特定列使用 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]}使用该键打印所有行。

如果键重复(打印另一个文件中没有键的所有行),这也适用,但行的顺序不会保留(行按键的字典顺序打印)。

相关内容