如何在 Unix 中比较两个 tsv 提取并打印密钥和标题的差异?

如何在 Unix 中比较两个 tsv 提取并打印密钥和标题的差异?

我有两个具有相同标题和结构的文件,第一列是键。

文件一:

key    val1 val2 val3 val4 val5
Item1  10   12   44   88   22
Item2  33   33   43   77   22
Item3  28   44   55   22   11
Item4  12   55   55   14   44

文件2:

key    val1 val2 val3 val4 val5
Item1  10   11   44   99   22
Item2  33   33   43   77   22
Item3  28   44   55   22   11
Item4  12   55   55   14   00

正如您在上面的文件中注意到的,这两个文件有两个不同之处:

  • item1 的 val2 和 val4 不同
  • 第 4 项的 val5 不同

所以我想生成一个比较输出,它应该告诉我哪个项目的哪些字段不同。输出应该类似于:

Item1: val2,val4
Item4: val5

答案1

awk -v ref=file1 '
    {
        getline refline <ref

        if (NR == 1) split(refline, head)

        nf = split(refline, a)

        for (i = 1; i <= nf; ++i)
            if ($i != a[i])
                diffcol[++n] = i

        if (n > 0) {
            $0 = a[1]

            for (i = 1; i <= n; ++i)
                $(i+1) = head[diffcol[i]]

            print
            n = 0
        }
    }' file2

这会将您的文件file2与“参考文件”进行比较file1。 from 的行file2通常由 读取awk,而 from 的行则通过在代码中调用并用 分割来file1显式读取。getlineawksplit

split(refline, head)块将数组设置head为标头,从 读取file1。这仅对来自 的第一行输入执行file2

第一个循环比较两个文件之间的字段以及任何不同且附加到diffcol数组的字段的列号。

如果发现一个或多个差异,则将输出 的第一个字段,后跟不同file1字段(来自 )的标题。file1

我编写代码的方式,标题行和第一个字段也受到比较,因此如果在第一个字段中发现差异,您也会得到一行输出,可能在key开始时该行的(因为这是 中第一个字段的值file1)。

该代码不假设两个文件中的列数相同。

鉴于您问题中的数据,您将从该代码中获得以下输出:

Item1 val2 val4
Item4 val5

答案2

paste使用 和的组合awk,假设所有记录具有相同数量的字段:

paste file1 file2 |
  awk -F'\t' '
    NR == 1 {cols = split($0, hdr) / 2; next}
    {
      diff = sep = ""
      for (i = 2; i <= cols; i++)
        if ($i "" != $(i+cols) "") {
          diff = diff sep hdr[i]
          sep = ","
        }
      if (sep) print $1": "diff
    }'

我们附加""到比较的每一侧,!=以便比较始终是词汇的(0不同于00or -0inffrom infinity、 1e500 等)。当字段看起来像数字时,删除这些字段以便比较为数字。

相关内容