【问题标题】:merge two files based on one column Awk基于一列 awk 合并两个文件
【发布时间】:2016-06-11 23:59:25
【问题描述】:

我正在尝试合并两个制表符分隔的文件文件——它们的长度不等。 我需要根据第 1 列合并文件,并将每个文件的第 3 列中的值获取到新文件中。如果任何文件缺少任何 id(不常见的值),那么它应该在新文件中获得一个空白值 -

File1: 
id1 2199 082
id2 0909 20909
id3 8002 8030
id4 28080 80828

File2:

id1 988 00808
id2 808 80808
id4 8080 2525
id6 838 3800

Merged file :

id1 082 00808
id2 20909 80808
id3 8030  
id4 80828 2525
id6   3800

我浏览了很多论坛和帖子,到目前为止我有这个

awk -F\t 'NR==FNR{A[$1]=$1; B[$1]=$1; next} {$2=A[$1]; $3=B[$1]}1'

但它不会产生正确的结果,任何人都可以建议。非常感谢!

【问题讨论】:

标签: awk


【解决方案1】:
$ awk -F'\t' 'NR==FNR{A[$1]=$3; next} {A[$1]; B[$1]=$3} END{for (id in A) print id,A[id],B[id]}' OFS='\t' File1 File2 | sort
id1     082     00808
id2     20909   80808
id3     8030
id4     80828   2525
id6             3800

工作原理

此脚本使用两个变量。对于 File1 中的每一行,关联数组A 都有一个键对应于 id 和第三个字段的值。对于 File2 中的每个 id,A 也有一个键(但不一定是值)。对于 File2,数组B 的每个 id 都有一个键,其对应的值来自第三列。

  • -F'\t'

    这会在输入到选项卡时设置字段分隔符。请注意,\t 必须被引用以保护它免受 shell 的影响。

  • NR==FNR{A[$1]=$3; next}

    这将为第一个文件设置关联数组A

  • A[$1]; B[$1]=$3

    这将为第二个文件设置关联数组。它还确保数组 A 对 file2 中的每个 id 都有一个键。

  • END{for (id in A) print id,A[id],B[id]}

    这会打印出结果。

  • OFS='\t'

    这会将输出字段分隔符设置为制表符。

  • sort

    不保证 awk 构造 for key in array 以任何特定顺序返回键。我们使用sort将输出排序为id中的升序。

【讨论】:

  • 非常感谢约翰!它完美无缺,您的解释确实有助于理解如何修改命令。
【解决方案2】:

假设没有重复的 ID,您可以尝试(在 -F$'\t' 使用 Bash):

awk -F$'\t' 'BEGIN { OFS=FS } NR==FNR { A[$1] = $3; next }
             { if ($1 in A) print $1, A[$1], $3
               else         print $1, " ", $3
               delete A[$1]
             }
             END { for (i in A) print i, A[i], " " }
            ' File1 File2

设置输出字段分隔符。对于第一个文件中的行,捕获数组 A 中第一个文件的字段 3,该字段由第 1 列索引。对于第二个(或后续)文件中的行,如果在 A 中找到 ID 列,则打印三个列;否则,打印一个空白来代替A 中缺少的条目。使用后删除A 中的条目。最后,对于第二个文件中缺少的条目,剩余的任何行都将打印为空白。

对于给定的数据,输出的一个例子是:

id1     082     00808
id2     20909   80808
id4     80828   2525
id6             3800
id3     8030

显然,如果您希望数据以特定方式排序,您可以使用sort 命令对awk 命令进行后过滤(意思是,将输出从awk 传送到sort)。

【讨论】:

  • 不需要 $'\t' 使其特定于 bash,awk 在这种情况下根据需要扩展 '\t'
  • 谢谢乔纳森,我不需要按排序顺序排列的结果,所以效果也很好!
【解决方案3】:

另一个类似的awk

$ awk -v OFS='\t' 'NR==FNR{a[$1]=$3; next} 
                          {$2=$3; $3=a[$1]; delete a[$1]} 1;   
                       END{for(k in a) print k,"",a[k]} ' file2 file1

id1     082     00808
id2     20909   80808
id3     8030
id4     80828   2525
id6             3800

首先使用 file2 来匹配给定的输出,但不能保证排序,如果您的键具有自然顺序,您可以按它们排序。

【讨论】:

  • 非常感谢 karakfa..现在我有几个 awk 选项可以学习!
【解决方案4】:

使用 GNU awk 获取 sorted_in:

$ cat tst.awk
BEGIN { FS=OFS="\t" }
{ a[$1] = (NR>FNR ? a[$1] OFS : "") $3 }
END {
    PROCINFO["sorted_in"] = "@ind_str_asc"
    for (k in a) {
        print k, a[k]
    }
}

$ awk -f tst.awk file1 file2
id1     082     00808
id2     20909   80808
id3     8030
id4     80828   2525
id6             3800

对于其他 awk,只需将输出通过管道传输到 sort

【讨论】:

    猜你喜欢
    • 2015-11-07
    • 2016-01-16
    • 2020-06-05
    • 1970-01-01
    • 2013-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多