使用 awk 删除基于两个字段的文件中的冗余

xgr*_*rau 2 arrays bash awk

我试图根据前两列的值删除一个非常大的文件(~100,000 条记录)中的重复行,而不考虑它们的顺序,然后打印这些字段+其他列。

所以,从这个输入:

A B XX XX
A C XX XX
B A XX XX
B D XX XX
B E XX XX
C A XX XX
Run Code Online (Sandbox Code Playgroud)

我想要:

A B XX XX
A C XX XX
B D XX XX
B E XX XX
Run Code Online (Sandbox Code Playgroud)

(也就是说,我想删除“BA”和“CA”,因为它们已经以相反的顺序出现;我不关心下一列中的内容,但我也想打印它)

我的印象是使用 awk + ​​数组应该很容易做到这一点,但我无法提出解决方案。

到目前为止,我正在修补这个:

awk '
NR == FNR {
h[$1] = $2   
next
}
$1 in h {
print h[$1],$2}' input.txt
Run Code Online (Sandbox Code Playgroud)

我将第二列存储在由第一个 (h) 索引的数组中,然后检查存储的数组中是否出现第一个字段。然后,打印该行。但是出了点问题,我没有输出。

我很抱歉,因为我的代码根本没有帮助,但我有点坚持这个。

你有什么想法?

非常感谢!

fed*_*qui 5

只需跟踪出现在两种格式上的内容:

$ awk '!seen[$1,$2]++ && !seen[$2,$1]++' file
A B XX XX
A C XX XX
B D XX XX
B E XX XX
Run Code Online (Sandbox Code Playgroud)

这相当于awk '!(seen[$1,$2]++ || seen[$2,$1]++)' file.

请注意,它也相当于没有++第二个表达式(见评论):

awk '!seen[$1,$2]++ && !seen[$2,$1]' file
Run Code Online (Sandbox Code Playgroud)

解释

打印独特线条的典型方法是:

awk '!seen[$0]++' file
Run Code Online (Sandbox Code Playgroud)

这将创建一个数组,seen[]其索引是到目前为止出现的行。因此,如果它是新的,seen[$0]则为 0 并增加到 1。但之前它被打印,因为表达式首先! var ++计算! var(并且在awk, True 触发打印当前行的操作)。当已经看到该行时,seen[$0]它具有正值,因此!seen[$0]为 false 并且不会触发打印操作。

在您的情况下,您想跟踪出现的内容,无论顺序如何,所以我正在做的是将索引存储在两个可能的位置。

  • @dev-null 是的,我终于崩溃并对其进行了测试,并发现在随后的一行中,`seen[b,a]` 已经是之前的 `seen[a,b]++` 中的 1。我更喜欢他们的剧本,因为它更清晰(至少对我来说)。 (3认同)
  • @EdMorton 你不需要最后一个`++`。考虑一下:`AB`、`AB`、`BA` -> `!0 && !0`、`!1 && !1`、`!0 && !2`。它可能很容易:`!0 && !0`、`!1 && !0`、`!0 && !2` (2认同)