挑选共享某些列的行(但不是全部)

时间:2017-11-22 14:41:35

标签: linux sorting awk unique

我正在尝试修改包含7列的文件。 输入文件示例为:

1.txt
    1   10  11  A   L   X3  -1.1
    1   10  11  A   L   X1   1.1
    1   13  21  A   T   X3  -2.1
    3   11  12  A   T   X2  -3.1
    3   11  12  K   T   X2   7.1
    4   11  12  A   T   X7  -8.1
    4   11  12  C   T   X7  -8.1
    4   11  12  C   T   X7  11.1

我想提取共享前5列的这些行,但最后两列不同,而其他不共享前5列的行。然后,我想在最后一列保留最低值的行。

预期输出为:

    1   10  11  A   L   X3  -1.1
    1   13  21  A   T   X3  -2.1
    3   11  12  A   T   X2  -3.1
    3   11  12  K   T   X2   7.1
    4   11  12  A   T   X7  -8.1
    4   11  12  C   T   X7  -8.1

1st line在此处,因为它与5文件中的2.line共享第一个1.txt列。并且它在最后一列(-1.1 < 1.1上的数字最小,而且对于最后一行,我们保留一个-8.1,因为它小于11.1),所以我们只保留它,我们保持其他第一个5字段不相同的行。 我尝试过的是将5列中的第index列保留为awk中的awk -F"\t" '!seen[$1,$2,$3,$4,$5]++' 1.txt ,但它只打印唯一的列,而不是其余的列。并且它不会选择最后一列中编号最小的行。 代码:

1   10  11  A   L   X3  -1.1
1   10  11  A   L   X1   1.1
1   13  21  A   T   X3  -2.1
3   11  12  A   T   X2  -3.1
3   11  12  K   T   X2   7.1
4   11  12  A   T   X7  -8.1
4   11  12  C   T   X7  -8.1
4   11  12  C   T   X7  11.1

其输出:

5

我无法选择仅共享第一列{{1}}列的行,这些列在最后一列上具有最低值。 感谢您的帮助!

2 个答案:

答案 0 :(得分:3)

awk救援! (在sort

的帮助下
$ sort -k1,5 -k7n file | 
  awk '!a[$1,$2,$3,$4,$5]++'


1   10  11  A   L   X3  -1.1
1   13  21  A   T   X3  -2.1
3   11  12  A   T   X2  -3.1
3   11  12  K   T   X2   7.1
4   11  12  A   T   X7  -8.1
4   11  12  C   T   X7  -8.1

对具有共享密钥(字段1到5)的记录进行排序,并按数字上升的第七个字段对它们进行排序(因此第一个字段具有最小值);通过管道传输到awk来获取给定密钥的第一条记录(着名的awk成语,您也在脚本中使用了。)

这是另一种没有awk

的方法
$ sort -k1,5 -k7n file | rev | uniq -f2 | rev

答案 1 :(得分:1)

awk '
    {key = $1 FS $2 FS $3 FS $4 FS $5} 
    !(key in min) || $NF < min[key] {min[key] = $NF; line[key] = $0} 
    END {for (key in line) print line[key]}
' file
    1   10  11  A   L   X3  -1.1
    1   13  21  A   T   X3  -2.1
    4   11  12  C   T   X7  -8.1
    4   11  12  A   T   X7  -8.1
    3   11  12  K   T   X2   7.1
    3   11  12  A   T   X2  -3.1

请注意输出的顺序是不确定的。您始终可以将输出传递给sort,或使用GNU awk并控制array traversal

我刚刚意识到line数组完全不必要,但会消耗大量内存:min数组将前5个字段作为键,第6个字段作为值

awk '
    {key = $1 FS $2 FS $3 FS $4 FS $5} 
    !(key in min) || $NF < min[key] {min[key] = $NF} 
    END {for (key in line) print key, min[key]}
' file

由于交换可能需要很长时间。