shell 排序去重问题

用 shell 处理一个文本文件,内容如下:

fdf     284 
asd     112
adf     146
csb     513
dfg     576
asd     346
adf     263
csb     092
dfg     547

根据第一列去重,相同的保留第二列值最大的那个,结果数据应该是这样的:

fdf    284
asd    346
adf    263
csb    513
dfg    576

看了下 uniq 命令,好像不支持按字段去重。请问该如何去重呢?

阅读 23.1k
6 个回答

方法一

cat data.txt | sort -rnk2 | awk '{if (!keys[$1]) print $0; keys[$1] = 1;}'

先按照第二列逆序排列,保证数字从大到小输出,然后再用 awk,只有第一列的字符串第一次出现才输出这个字符串,其他的丢弃,这样应该就可以解决问题了。但是这种方法可能会让 awk 占用很多的内存,文件过大的话有问题。

方法二

cat data.txt | sort -k1,1 | awk '{
    if (lastKey == $1) {
        if (lastValue < $2) {
            lastLine = $0;
            lastValue = int($2);
        }
    } else {
        if (lastLine) {
            print lastLine;
        }

        lastKey = $1;
        lastLine = $0;
        lastValue = int($2);
    }
} END {
    if (lastLine) {
        print lastLine;
    }
}'

这个方案是按照第一列排序,然后用 awk 筛选结果,筛选的过程相当于一个加强版的 uniq。这个方案在内存使用方面好了很多,不过代码量略多,不是很简洁。

$ sort -r a.txt | awk '{print $2, $1}' | uniq -f1 | awk '{print $2, $1}'
fdf 284
dfg 576
csb 513
asd 346
adf 263

逆排, 反转第一列和第二列, 按第二列去重, 反转第一列和第二列

awk  'BEGIN{ a[$1]=$2 }{ if ($2>a[$1] )  a[$1]=$2  }END{for (i in a) if (i)  print i,a[i]}' data.txt   

把第一列放入数组 然后对比数组的值 大的就替换掉为新的值

不用uniq,用sort命令可以实现,这是一个按列去重的问题。

sort -r -n -k2,2 raw.txt | sort -u -k1,1

解释:先按第二列数字大小逆序,然后再按第一列去重,这样就能得到第二列最大的非重复行数据。

结果:
adf 263
asd 346
csb 513
dfg 576
fdf 284

~$ echo -e 'fdf     284 \nasd     112\nadf     146\ncsb     513\ndfg     576\nasd     346\nadf     263\ncsb     092\ndfg     547' | sort -r | sort -k1,1 -u
adf     263
asd     346
csb     513
dfg     576
fdf     284
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏