如何使用awk根据两列查找唯一值



考虑我有一个测试文件(test.txt)包含的数据如下

1,2 
2,3 
2,1 
2,2 
3,1 
1,3 
2,5
4,1

我希望删除每对重复项,例如对于对 (1,2) 和 (2,1),应该打印其中任何一个(先到先打印)。预期输出为

1,2
2,3
3,1
2,2
2,5
4,1

我已经尝试过这个命令,awk -F"," '!seen[$1,$2]++ && !seen[$2,$1]' test.txt.它打印为

1,2
2,3
3,1
2,5
4,1

为什么 2,2 对没有打印?还有如何获得预期的输出。

如前所述,问题来自逻辑评估中的短路。

为了规避这种情况,一种选择是在 2D 表seen中测试和设置一个值:

awk -F"," '!seen[($1<$2?$1:$2)+0, ($1>$2?$1:$2)+0]++'

基本上,它使用两个值中的最小值和最大值作为索引,因此一个测试而不是两个测试,然后递增插槽。

请注意强制转换为整数的+0指令。必须这样做,因为字段包含额外的空格,包括最终的行尾。

常见的、惯用的 2 字段解决方案,在任何 UNIX 盒子上的任何 shell 中具有任何 awk:

$ awk -F, '!seen[$1>$2 ? $1 FS $2 : $2 FS $1]++' file
1,2
2,3
2,2
3,1
2,5
4,1

对于使用 GNU awk 进行asort()的任意数量的字段:

awk -F, '{split($0,a); asort(a); for (i=1;i<=NF;i++) k=(i>1 ? k FS : "") a[i]} !seen[k]++' file

例如,对于包含 1,2,3 和 2,3,4 的每个排列的输入文件:

$ cat file
1,2,3
1,3,2
2,1,3
2,3,1
3,1,2
3,2,1
2,3,4
2,4,3
3,2,4
3,4,2
4,2,3
4,3,2
$ awk -F, '{split($0,a); asort(a); for (i=1;i<=NF;i++) k=(i>1 ? k FS : "") a[i]} !seen[k]++' file
1,2,3
2,3,4

>我不擅长awk,但使用其他工具很容易做到这一点。

如果顺序无关紧要,让我们复制每行,顺序颠倒。然后我们可以只用sort -u来打印独特的行:

cat <<EOF |
1,2
2,3
2,1
2,2
3,1
1,3
2,5
4,1
EOF
sed 's/(.*),(.*)/1,2:1,2n2,1:1,2/' | 
sort -u -t: -k1,1 |
cut -d: -f2 |
sort -u

sedutlity 变换了这条线:

1,2

到:

1,2:1,2
2,1:1,2

然后我使用第一列删除重复项,仅用:sort -u -t: -k1,1分隔。然后cut -d: -f2删除第一列。然后我再次对唯一行进行排序,因为我们会有重复的行(例如,当1,2对于1,22,1都是唯一的)。结果是:

1,2
2,2
2,3
2,5
3,1
4,1

如果顺序不重要,您可以轻松使用awksortuniq组合。

awk -F "," '$1 < $2 { printf "%d,%dn", $1,$2} $2 <= $1 {printf "%d,%dn", $2, $1}' file | sort | uniq

此处的 awk 操作通过对数字进行排序来规范输出,以便2,1变得1,2

最新更新