使用awk循环遍历共享字段的行

3
我将尝试编写一个 awk 脚本,它可以接收所有共享公共字段(例如第一个字段)的行,并循环两次以在它们之间创建所有可能的组合。
最好通过以下示例进行说明 - 给定以下输入:
cat input.txt

A this
A text
B MORE
B THINGS

我希望脚本能够构建所有以"A"开头的行的可能组合,然后是以"B"开头的行。因此,这将是输出结果:
cat output.txt

A this A this
A text A this
A this A text
A text A text
B MORE B MORE
B THINGS B MORE
B MORE B THINGS
B THINGS B THINGS

所有可能的"this"和"text"组合 + "MORE"和"THINGS"的组合

到目前为止,我已经得到了这个:

awk '{pair[++c] = $0 } END {
    for ( i = 1; i <= c; i++ ) {
        for ( j = 1; j <= c; j++ ){
            print pair[j], pair[i]
        }
    }
}' input.txt > output.txt

但是输出结果为:
A this A this
A text A this
B MORE A this
B THINGS A this
A this A text
A text A text
B MORE A text
B THINGS A text
A this B MORE
A text B MORE
B MORE B MORE
B THINGS B MORE
A this B THINGS
A text B THINGS
B MORE B THINGS
B THINGS B THINGS

它不考虑第一个字段是"A"还是"B",只是生成所有的组合。

有什么想法吗?

我考虑过根据第一个字段拆分输入文件,并对每个拆分文件进行双重循环。但我需要一个也适用于具有许多不同值的第一个字段的非常长的文件的解决方案。

谢谢!

2个回答

2
使用GNU awk处理二维数组:
$ cat tst.awk         
{ vals[$1][$0] }
END {
    for (key in vals) {
        for (val1 in vals[key]) {
            for (val2 in vals[key]) {
                print val1, val2
            }
        }
    }
}

$ gawk -f tst.awk file
A this A this
A this A text
A text A this
A text A text
B MORE B MORE
B MORE B THINGS
B THINGS B MORE
B THINGS B THINGS

如果您的第一列始终有序,您可以使用任何awk来执行此操作,并且它将使用更少的内存:

$ cat tst.awk
$1!=prev { shuffle() }
{ vals[$0]; prev=$1 }
END { shuffle() }

function shuffle(       val1, val2) {
    for (val1 in vals) {
        for (val2 in vals) {
            print val1, val2
        }
    }
    split("",vals)
}

$ awk -f tst.awk file
A this A this
A this A text
A text A this
A text A text
B MORE B MORE
B MORE B THINGS
B THINGS B MORE
B THINGS B THINGS

1
第一个方法非常好用,非常感谢!是的,我使用的是gawk。第二个解决方案也可以,但我并不总是有排好序的文件。+1 - xgrau

1
你可以避免使用数组:
cat input.txt| while read f1 fn; do
   awk '/^'$f1'/ {print "'$f1' '$fn' "$0 }' input.txt
done

编辑:将input-file更改为input.txt并添加无awk的解决方案:

对于每一行,都会启动awk,因此此解决方案可能较慢(您可以进行测试)。 没有awk的解决方案将在每个循环中启动一个额外的程序:

cat input.txt| while read f1 fn; do
   grep "^$f1" input.txt | sed 's/^/'"${f1} ${fn}"'/'
done

我想我明白了你的答案,但我不确定每个文件(inputfile)是什么。它们都是我所提到的同一个input.txt文件吗? - xgrau
@XGrau 是的,你说得对。我已经改了,并添加了另一个解决方案。我猜对于长文件来说速度会非常慢,但不会耗尽内存。 - Walter A
很好,两者都完美地工作。它们的确比较慢,但对于小问题可能会起到一定的作用。我想投票,但声望不够 - 不管怎样,谢谢!@walter-a - xgrau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接