使用 AWK 合并两个文件并加上第二列

使用 AWK 合并两个文件并加上第二列

我想合并两个文件并加上两个文件的第二列。

文件1。

001    A
002    B
003    C
004    D

文件2。

002    D
003    D
005    E
006    F

应合并到文件 3 中,如下所示。

001    A
002    BD
003    CD
004    D
005    E
006    F

我运行以下命令,但输出不正确

$ awk 'FNR==NR{a[$1]=$2; next}{print $1, a[$1]$2}' file1 file2
002    BD
003    CD
005    E
006    F

请帮助我如何合并使用 awk 或 sed。

答案1

左/右外连接与全外连接

您使用的命令的问题是它要求 file2 中的所有条目都位于 file1 中。该语句的打印部分仅评估 file2 中的条目。

FNRNR经常以这种方式用于基于共性的 JOIN - 从一个数据集中选取所有记录,并仅从另一个数据集中选取相关记录。您所实现的具体是“RIGHT OUTER JOIN”,因为它将从“右侧”拉入所有条目 - file2,以及左侧 file1 的匹配成员。

相反,您想要进行“FULL OUTER JOIN”。两个文件中的所有记录均基于第 1 列合并记录。

FNR==NR表示“FILE”记录数 ( FNR) 等于记录总数 ( NR)。NR处理任何文件中的每一行都会递增,而FNR在启动新文件时将重置为 0。FNR==NR只有在读入第一个文件的同时导入 2 个以上文件时才如此。当 awk 切换到下一个文件时,FNR 将重置为 0,而 NR 将继续增长。

为了说明这一点,我插入了一条打印语句,以在 awk 处理输入时提供这些变量的状态:

$> awk 'FNR==NR{a[$1]=$2;printf("File: %s, NR: %s, FNR: %s, $1: %s, $2: %s, a[$1]: %s\n",FILENAME,NR,FNR,$1,$2,a[$1]); next}  {printf("File: %s, NR: %s, FNR: %s, $1: %s, $2: %s, a[$1]$2: %s\n",FILENAME,NR,FNR,$1,$2,a[$1]$2); }' file1 file2

File: file1, NR: 1, FNR: 1, $1: 001, $2: A, a[$1]: A
File: file1, NR: 2, FNR: 2, $1: 002, $2: B, a[$1]: B
File: file1, NR: 3, FNR: 3, $1: 003, $2: C, a[$1]: C
File: file1, NR: 4, FNR: 4, $1: 004, $2: D, a[$1]: D
File: file2, NR: 5, FNR: 1, $1: 002, $2: D, a[$1]$2: BD
File: file2, NR: 6, FNR: 2, $1: 003, $2: D, a[$1]$2: CD
File: file2, NR: 7, FNR: 3, $1: 005, $2: E, a[$1]$2: E
File: file2, NR: 8, FNR: 4, $1: 006, $2: F, a[$1]$2: F

解决方案

要解决这个问题,您需要做的就是在处理 file2 时继续向数组添加条目,并在处理完所有输入文件后才输出结果。

所以在这种情况下我们实际上并不关心NRFNR根本不关心。

  • 对于所有输入文件中的每一行文本,使用其第一列值$1作为数组的索引a[$1]

  • 将第 2 列的值分配$2给该索引处的数组,但附加该值,这样我们就不会覆盖可能已存在的值:a[$1]=a[$1]$2

  • 等到所有记录/行都处理完毕后再打印出数组:

    for (i in a) { printf("%s\t%s\n", i, a[i]) }

唯一的缺点是 awk 使用关联数组,该数组使用基于字符串的索引,而不是整数(这就是它起作用的原因),但其副作用是数组中条目的顺序可能不符合预期;在这种情况下,打印出的内容不是按数字顺序(按索引),因此需要通过管道输出进行排序:

$> awk '{ a[$1]=a[$1]$2; next } END { for (i in a) { printf("%s\t%s\n", i, a[i]) } }' file1 file2 | sort -n 

001     A
002     BD
003     CD
004     D
005     E
006     F

替代方法

您还可以使用 join 命令来执行此操作,但我不知道如何让它组合字段 - 它们仍然以空格分隔,因此需要额外的处理阶段:

$> join  -o 0,1.2,2.2 -a1 -a2 file1 file2 | awk '{printf("%s\t%s%s\n", $1, $2, $3)}'
001     A
002     BD
003     CD
004     D
005     E
006     F

去做

这对于拒绝重复条目没有任何作用 - 这可能是也可能不是所需的。目前,如果单独的输入文件中有重复的记录,它们将被合并:

file1: 001 A
并将
file2: 001 A
导致输出记录
001 AA

答案2

现在我已经看到了您的研究成果,我给您我的解决方案:

awk '{ z[$1]=z[$1]$2 } END { for (i in z) print i, z[i] }' file1  file2

输出是:

002 BD
003 CD
004 D
005 E
006 F 
001 A

如果要对第一列的值进行数字排序,可以将上一个命令的输出传递给sort

awk '{ z[$1]=z[$1]$2 } END { for (i in z) print i, z[i] }' file1  file2 \
| sort -n -k1

相关内容