基于一列Awk合并两个文件

基于一列Awk合并两个文件,awk,Awk,我正在尝试合并两个以制表符分隔的文件-它们的长度不等。 我需要根据第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 00

我正在尝试合并两个以制表符分隔的文件-它们的长度不等。 我需要根据第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 -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
工作原理 此脚本使用两个变量。对于文件1中的每一行,关联数组
A
都有一个键,对应于第三个字段的id和值。对于文件2中的每个id,
A
也有一个键(但不一定是值)。对于文件2,数组
B
对每个id都有一个键,该键具有第三列中的相应值

  • -F'\t'

    这会将输入时的字段分隔符设置为选项卡。请注意,必须引用
    \t
    ,以保护它不受外壳的影响

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

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

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

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

  • END{for(A中的id)打印id,A[id],B[id]}

    这会打印出结果

  • OFS='\t'

    这会将输出字段分隔符设置为选项卡

  • 排序

    数组中键的awk构造
    不保证以任何特定顺序返回键。我们使用
    sort
    在id中将输出按升序排序


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

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

对于给定数据,输出示例如下:

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

显然,如果您希望以特定的方式对数据进行排序,可以使用
sort
命令对
awk
命令进行后期筛选(这意味着将
awk
的输出通过管道输送到
sort
)。

另一个类似的
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

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

使用GNU awk for
排序

$ 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,只需通过管道将输出传输到
排序

无需
$'\t'
使其特定于bash,在本例中,awk会根据需要扩展
'\t'
。谢谢Jonathan,我不需要按排序顺序显示结果,因此也可以正常工作!非常感谢约翰!它工作得非常完美,您的解释真的有助于理解如何修改命令。非常感谢karakfa。现在我有两个awk选项可供学习!请注意,在这里说“谢谢”的首选方式是投票选出好的问题和有帮助的答案,并接受对你提出的任何问题最有帮助的答案(这也会给你的声誉带来一点提升)。请参阅本页,以及
$ 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