给制表符表格第二列重复字段加(dupl)后缀,求高效单命令实现
高效处理制表符文件重复字段的单Awk方案
问题背景
现有制表符分隔的输入文件内容如下:
raw1 aaa wer raw2 bbb dfg raw3 ccc fgh raw4 ccc etr raw5 aaa cbg raw6 aaa dfg
需求明确:只要第二列的字段值在该列重复出现,就给它加上(dupl)后缀,预期输出如下:
raw1 aaa(dupl) wer raw2 bbb dfg raw3 ccc(dupl) fgh raw4 ccc(dupl) etr raw5 aaa(dupl) cbg raw6 aaa(dupl) dfg
当前使用的多管道代码处理大文件时效率极低:
cut -d$'\t' -f2 input|\ sort|\ uniq -c|\ awk '$1>1{print $2}'|\ while read dup;do gawk -F$'\t' -i inplace -va=$dup '$2==a{$2=a"(dupl)"}1' OFS=$'\t' input;done
需要一个无需多管道、基于单Awk或Sed的高效实现方式。
单Awk解决方案
用Awk的两次扫描逻辑即可完成,全程仅遍历文件两次,效率远高于原方案:
awk -F'\t' -v OFS='\t' ' # 第一次扫描:统计第二列各值的出现次数 NR == FNR { count[$2]++; next } # 第二次扫描:根据计数判断是否添加后缀 { if (count[$2] > 1) $2 = $2 "(dupl)"; print } ' input input
细节说明
NR == FNR是Awk中判断首次读取文件的经典写法,这一步仅做统计,把第二列每个值的出现次数存入count数组。- 第二次读取文件时,检查当前行第二列的计数,只要次数大于1,就给该字段加上
(dupl)后缀,随后输出整行。 -F'\t'和OFS='\t'确保输入输出均为制表符分隔,不会破坏原文件的格式。
如果需要直接原地修改文件(无需输出到新文件),可使用Gawk的-i inplace选项:
gawk -i inplace -F'\t' -v OFS='\t' ' NR == FNR { count[$2]++; next } { if (count[$2] > 1) $2 = $2 "(dupl)"; print } ' input input
效率对比
原方案的核心问题是:每个重复值都会单独启动一次Awk遍历文件,大文件下IO开销会急剧上升。而单Awk方案仅需遍历两次文件,内存仅需存储第二列的唯一值及其计数,无论文件规模多大,性能都能保持稳定。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

