You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Shell中基于指定字段对CSV去重(保留首次出现):求简化方案

简洁实现按年龄去重并保留首次出现条目

先确认一下你的文件内容emails.csv:

cat emails.csv
1,joe,joe@gmail.com,32
2,jim,jim@hotmail.fr,23
3,steve,steve_smith@temporary.com.br,45
4,joseph,joseph@protonmail.com,23
5,jim,jim29@bluewin.ch,29
6,hilary,hilary@bluewin.ch,32

你的需求是:基于最后一个字段(年龄)去重,只保留每个年龄首次出现的条目,期望输出:

1,joe,joe@gmail.com,32
2,jim,jim@hotmail.fr,23
3,steve,steve_smith@temporary.com.br,45
5,jim,jim29@bluewin.ch,29

你原来的双循环脚本确实太复杂了,下面是两个高效简洁的解决方案:

方法1:Awk一行命令(最推荐)

Awk天生就是处理这类文本数据的神器,一行命令直接搞定,而且只需要遍历文件一次,效率比双循环高太多:

awk -F, '!seen[$NF]++' emails.csv

为啥这行命令能行?

  • -F,:告诉awk用逗号作为字段分隔符
  • $NF:表示当前行的最后一个字段(也就是年龄)
  • seen[$NF]:我们用一个叫seen的数组来记录每个年龄是否已经出现过
  • !seen[$NF]++:当这个年龄第一次出现时,seen[$NF]的值是0,取反后为真,awk就会默认打印当前行;然后seen[$NF]会自增为1,后面再遇到相同年龄时,这个条件就为假,不会再打印了

执行后输出完全符合你的期望,而且还保持了原文件的顺序,不需要额外排序~

方法2:纯Bash关联数组实现

如果你不想用awk,用Bash本身的关联数组也能实现,比你的双循环简洁多了:

declare -A seen
while IFS=, read -r id name email age; do
    if [[ -z "${seen[$age]}" ]]; then
        echo "$id,$name,$email,$age"
        seen[$age]=1
    fi
done < emails.csv

解释一下:

  • declare -A seen:声明一个关联数组,用来记住已经处理过的年龄
  • IFS=, read -r id name email age:按逗号分隔读取每一行的四个字段,IFS=是为了保留可能的空格(虽然这里没有),-r是防止转义字符被解析
  • 每次读取一行后,检查当前年龄是否在seen数组里,如果没出现过,就打印这一行,然后把这个年龄标记为已出现

这个方法同样能得到你想要的结果,而且也是只遍历一次文件。

这两种方法都比你原来的双循环方案简洁高效,尤其是处理大文件的时候,差距会特别明显~

内容的提问来源于stack exchange,提问作者Pedro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:13:08