awk '{count[$0]++} end {for (line in count) if (count[line] > 1) print count[line], line}' 能准确统计所有重复行及次数,无需预排序,保留原始顺序和结构;而 sort | uniq -c 必须先排序,会丢失行号与上下文。

直接用 awk '{count[$0]++} END {for (line in count) if (count[line] > 1) print count[line], line}' file 就能准确统计所有重复行及其出现次数,无需预排序,且保留原始行内容的完整结构。
为什么不用 sort | uniq -c?
它要求文件必须先排序,否则 uniq 只能识别相邻重复行。比如原始文件是:
apple banana apple orange apple
执行 sort file | uniq -c 会把三行 apple 合并成一行计数,但你可能需要知道它们原本分散在哪几处;更重要的是,一旦排序,行号、上下文顺序全丢,后续做日志定位或关联分析就断链了。
而 awk 方案天然按实际读取顺序遍历,count[$0] 对整行哈希计数,完全规避排序依赖。
awk 统计重复行的三种常用写法差异
以下命令都有效,但适用场景和输出格式不同:
-
awk '{a[$0]++} END {for (i in a) if (a[i]>1) print a[i], i}' file:输出无序(for (i in a)遍历哈希表不保证顺序),适合只看结果 -
awk '{a[$0]++} END {for (i in a) if (a[i]>1) print a[i], i}' file | sort -nr:加管道按次数降序排,-n防止把"10 apple"当字符串排在"2 banana"前面 -
awk '{a[$0]++; b[$0]=NR} END {for (i in a) if (a[i]>1) print a[i], i, "first@line", b[i]}' file:额外记录首次出现行号b[$0]=NR,方便溯源
按某一列统计重复(比如用户ID、IP、邮箱)
如果文件是空格/制表符分隔,想看第2列重复情况,别直接 <p>如果文件是空格/制表符分隔,想看第2列重复情况,别直接 <code>$0,改用 $2:
:例如日志中提取重复的客户端 IP:
awk '{ip[$2]++} END {for (i in ip) if (ip[i] > 1) print ip[i], i}' access.log
注意:$2 默认以空白分割;若字段用冒号分隔(如 /etc/passwd),得加 -F::
awk -F: '{user[$1]++} END {for (u in user) if (user[u] > 1) print user[u], u}' /etc/passwd
常见坑:$1 前后有空格时会被截断,必要时用 gsub(/^[ \t]+|[ \t]+$/, "", $1) 清理,但会拖慢性能——真有脏数据,优先用 cut -d' ' -f1 | sed 's/^ *//;s/ *$//' | sort | uniq -c 更稳。
性能与大文件注意事项
awk 把整行当 key 存进内存哈希表,行越多、行越长,内存占用越高。处理 GB 级日志时容易 OOM:
- 确认是否真需全量统计:可先用
head -n 1000000 file | awk ...抽样评估重复分布 - 避免在
END块里做复杂操作(如拼接长字符串、调用系统命令),会显著拖慢 - 若只要“有没有重复”,用
awk 'seen[$0]++ == 1 {print "duplicate:", $0; exit}' file遇到第一个重复就退出,省资源
真正难的不是写对命令,而是判断该不该用 awk——当行内容含不可见字符(如 \r)、编码混杂(UTF-8 + GBK)、或字段内含分隔符时,$0 或 $N 的行为会出人意料,这时宁可用 Python 的 csv 模块或 iconv 预处理。











