这是我大约 24 小时前提出的上一个问题的后续问题:在文件之间同时匹配两个主列,并在这些主列匹配时将补充列粘贴到输出文件中
G-Man 用一个有用的代码解决了这个问题,但我有一个后续问题。我已经接受了答案,因此第二篇文章......
我有 3 个文件,每个文件都有唯一数量的列,所有列均以制表符分隔,但某些列在 3 个文件之间共享。这是我想用来创建某种“聚合”文件的 3 个文件之间的共享列。
下表显示了这些文件的示例。基本上我想匹配文件之间的 MAIN1 和 MAIN2 列。三个文件之间的两列都必须匹配。
当两个文件之间的 MAIN1 和 MAIN2 匹配时,我想将 file2 中的列“minor8”添加到 file1 中表格的右侧。随后,当两个文件之间的 MAIN1 和 MAIN2 匹配时,我想在 file1 表的右侧添加 file3 中的“minor9”。因为“minor8”应该紧邻 file1 的最右列(列名称:“minor3”),所以我希望“minor9”紧邻“minor8”进入新的输出文件。输出文件给出了我理想的最终文件应该是什么样子的想法。
基本上这些是 3 个文件的示例(“选项卡”有点混乱)
文件1:
MAIN1 minor1 MAIN2 minor3
1 bla1 a blabla1
1 bla2 b blabla2
1 bla3 c blabla3
2 bla4 a blabla4
2 bla5 d blabla5
3 bla6 e blabla6
4 bla7 f blabla7
5 bla8 a blabla8
5 bla9 g blabla9
文件2:
minor8 MAIN1 MAIN2
yes1 2 d
yes2 3 e
yes3 4 f
yes4 5 a
yes5 5 g
yes6 1 a
yes7 1 b
yes8 1 c
yes9 2 a
文件3:
MAIN1 MAIN2 minor9
5 a sure1
5 g sure2
1 a sure3
1 b sure4
1 c sure5
2 a sure6
2 d sure7
3 e sure8
4 f sure9
所需的输出文件:
MAIN1 minor1 MAIN2 minor3 minor8 minor9
1 bla1 a blabla1 yes6 sure3
1 bla2 b blabla2 yes7 sure4
1 bla3 c blabla3 yes8 sure5
2 bla4 a blabla4 yes9 sure6
2 bla5 d blabla5 yes1 sure7
3 bla6 e blabla6 yes2 sure8
4 bla7 f blabla7 yes3 sure9
5 bla8 a blabla8 yes4 sure1
5 bla9 g blabla9 yes5 sure2
如前所述,G-Man 提供了一个有用的代码,它完全符合我的要求(请参阅上一篇文章)。我可能会问 G-Man(或其他有时间的人)一些关于我不太理解的代码中某些单独行的具体问题,但在那之前,我有后续问题。
G-Man 的代码能够重新创建上述 OUTPUT 文件,所以谢谢 G-Man!
后续问题:
我忘记提及的一件事是,代码无法执行(据我所知),如果文件之间的 MAIN1 和 MAIN2 列不匹配,它将从 file1 中删除行。这是我的错,因为我没有具体说明这一点。我的目标是拥有一个输出文件,其中 file1 中的任何行都不会被删除。
基本上 file1 是我的优先文件。无论该文件有多少行(接近一百万),这也是 OUTPUT 文件应该有的行数。如果没有列 MAIN1、MAIN2 匹配,则某些行的“minor8”和“minor9”列可以为空。但当“minor8”或“minor9”(或两者)存在“缺失/空”值时,我想保留 file1 的这些行。
我将尝试使用上述文件 2 和 3 的稍微不同的版本来说明这一点(因此 file1 保持不变)。
调整后的文件2(没有MAIN1,MAIN2组合:2,d):
minor8 MAIN1 MAIN2
yes2 3 e
yes3 4 f
yes4 5 a
yes5 5 g
yes6 1 a
yes7 1 b
yes8 1 c
yes9 2 a
调整后的文件3(没有MAIN1,MAIN2组合:5,a):
MAIN1 MAIN2 minor9
5 g sure2
1 a sure3
1 b sure4
1 c sure5
2 a sure6
2 d sure7
3 e sure8
4 f sure9
调整后的所需输出(即,对于 MAIN1、MAIN2 组合 2-d,minor8 列中为空值;对于 MAIN1、MAIN2 组合 5-a,minor9 列中为空值):
MAIN1 minor1 MAIN2 minor3 minor8 minor9
1 bla1 a blabla1 yes6 sure3
1 bla2 b blabla2 yes7 sure4
1 bla3 c blabla3 yes8 sure5
2 bla4 a blabla4 yes9 sure6
2 bla5 d blabla5 sure7
3 bla6 e blabla6 yes2 sure8
4 bla7 f blabla7 yes3 sure9
5 bla8 a blabla8 yes4
5 bla9 g blabla9 yes5 sure2
我希望我的解释方式足够清楚。我发现表格的选项卡有点混乱。你们喜欢这样,还是喜欢我在视觉上理顺表格? (我可以想象,由此产生的唯一问题是,当您复制粘贴我的示例数据时,您将拥有不应该存在的其他选项卡......)
无论如何,我非常感谢你们的帮助。希望在不久的将来的某个时候,除了简单地寻求帮助之外,我还能为这个论坛做出贡献......
您对如何编辑 G-Man 的代码以使这成为可能有什么建议吗?或者,如果您对如何编写考虑到此附加要求的有用代码有完全不同的建议,请告诉我。
答案1
创建以下文件:
merge21
:
开始 { FS =“\t” OFS =“\t” } NR==FNR { #文件2 关键=$2“,”$3 存在[键] = 1 小8[键] =1 美元 下一个 } {#文件1 键 = $1 "," $3 if (present[key]) 打印 $1, $2, $3, $4,小调8[调] 否则打印$1、$2、$3、$4、“-” }
merge312
:
开始 { FS =“\t” OFS =“\t” } NR==FNR { #文件3 关键=$1“,”$2 存在[键] = 1 小9[键] =3 美元 下一个 } {#文件1 + 文件2 键 = $1 "," $3 if (present[key]) 打印 $1, $2, $3, $4,$5,未成年人9[关键] 否则打印 $1, $2, $3, $4,5 美元, “-” }
它们几乎完全相同;我已将差异加粗。现在输入命令
awk -f merge21 file2 file1 | awk -f merge312 file3 -
这假设您的关键字段都不包含逗号,并且您的数据都不包含连字符,但这实际上仅取决于存在一些数据中未出现的字符串。扩展它以支持更多列将是微不足道的;我希望这是显而易见的。这可以可以增强在一次运行中完成所有操作awk
,但这会更复杂一些,并且(IMNSHO)不值得付出努力。
这会产生文件中数据的所谓“左外连接”;看INNER 和 OUTER 连接之间的区别在 Stack Overflow 上了解一些定义。 (“左外连接”在该问题的接受答案中定义为(解释为)“第一个表中的所有行,加上其他表中的任何公共行”。)
你的输出将是
MAIN1 minor1 MAIN2 minor3 minor8 minor9
1 bla1 a blabla1 yes6 sure3
1 bla2 b blabla2 yes7 sure4
1 bla3 c blabla3 yes8 sure5
2 bla4 a blabla4 yes9 sure6
2 bla5 d blabla5 - sure7
3 bla6 e blabla6 yes2 sure8
4 bla7 f blabla7 yes3 sure9
5 bla8 a blabla8 yes4 -
5 bla9 g blabla9 yes5 sure2
并且,显然,您可以删除-
字符与sed
. (当然,如果您的真实数据实际上包含连字符,请选择一些未使用的字符或字符串作为缺失数据的占位符。)
笔记
FS
和OFS
分别是输入字段分隔符和输出字段分隔符。 (显然IFS
在 中毫无意义awk
;这是我的一个错误。)您可能并不真正需要FS="\t"
—awk
默认情况下将制表符识别为输入中的字段分隔符。 (它允许您拥有包含空格的字段,但您似乎对此不感兴趣。)OFS="\t"
很重要;因此,我可以说print $1, $2, $3, $4
并让输入字段成为输出它们之间有标签。如果我不说OFS="\t"
,它们将被空格分隔,除非我说print $1 "\t" $2 "\t" $3 "\t" $4
,这是乏味的并且损害可读性。如果您对 MAIN1 和 MAIN2 给出了额外的约束 - 例如,它们始终只有一个字符,或者 MAIN1 始终是数字而 MAIN2 始终以字母开头 - 我就不需要
,
中的逗号 ( )key
。但你的第一个问题的原始版本没有显示这样的限制。考虑以下数据:MAIN1 ($2) MAIN2 ($3) badkey = $2 $3 goodkey = $2 "," $3 2 34151 234151 2,34151 23 4151 234151 23,4151
如果我们不在键中包含一些不会出现在键字段(MAIN1 和 MAIN2)中的分隔符,我们可以
key
为不同的行获得相同的值。- 冒着吹毛求疵的风险,我不会“告诉 Linux”任何事情;我只是想告诉 Linux。我告诉你
awk
该怎么做。 - 关于代码
NR==FNR { # 文件3 键 = $1 "," $2 存在[键] = 1 小9[键] = $3 下一个 }
考虑 的倒数第七行file3
,其中包含1 a sure3
。显然我们有$1
=1
、$2
=a
和$3
=sure3
,所以key
=1,a
。present[key] = 1
意味着我设置present["1,a"]
为1
一个标志来表明file3
有一条1,a
线;即=有一个minor9
值。由于 中 没有行,没有被设置,因此代码的“”部分知道没有for = ,并且应该打印key
1,a
5,a
file3
present["5,a"]
file1 + file2
minor9
key
5,a
-
反而。这个名字present
只是我随意选择的;它表明该1,a
行是展示在file3
(并且该5,a
行不在)。习惯上用1
“TRUE”来表示。 您可以替换
print $1, $2, $3, $4
为for (n=1; n<=4; n++) printf "%s\t", $n
.您应该通过对最后一个字段使用 plainprint
(而不是)或通过执行来结束该行。您可以通过执行类似的操作进一步简化printf
printf "\n"
for (n=1; n<=4; n++) printf "%s\t", $n if (present[key]) 打印minor8[key] 否则打印“-”
请阅读awk(1), 这POSIX 规范awk
,
GNU Awk 用户指南,并查看awk.info了解更多信息。