awk 输出的浮点数默认是 %.6g:一次优选 IP 排名全乱的排查
awk 输出的浮点数默认是 %.6g:一次优选 IP 排名全乱的排查
现象
2026 年 10 月 29 日,在做 Cloudflare 优选 IP(新加坡节点)排名时,脚本算出的综合分明明是几千万量级,sort -n 排出来的顺序却全乱了:一个 4.13e+07 分的 IP 排在了 9.8e+06 分的 IP 前面,而按数值算前者应该是后者的 4 倍多。排名结果直接进了当天的优选名单,差点把一批低分 IP 当成高分 IP 发布出去。
定位
出问题的链路是典型的 shell 管道:
awk '{ score = $2 * 1000000 + $3; print score, $1 }' raw.txt | sort -n -k1,1 > ranked.txt
单独看 awk 的输出就露馅了:
4.13e+07 104.18.32.5
9.8e+06 104.16.11.9
awk 直接 print 一个计算出的浮点数时,默认按 %.6g 格式化输出——6 位有效数字,超过就转科学计数法。而 sort -n 解析数字时只认前缀,4.13e+07 被当成 4.13,9.8e+06 被当成 9.8,于是 4.13 < 9.8,排名彻底颠倒。更隐蔽的是:分值在 1e6 以下时输出是普通小数,排序看着全对;只有分值跨过科学计数法阈值的那一批会错,问题藏在数据量最大的那天才爆出来。
修复
凡是算出来要参与排序、比较或写入下游的数,输出时必须显式指定格式,不要交给 print 的默认格式化:
awk '{ score = $2 * 1000000 + $3; printf "%.0f %s\n", score, $1 }' raw.txt | sort -n -k1,1 > ranked.txt
printf "%.0f" 强制输出定点整数,sort -n 拿到的就是完整数值。修复后同一批数据重排,顺序与手工验算一致。
如果分值本身需要保留小数,同样要定死格式,例如 printf "%.3f %s\n",原则不变:格式由你定,不由 awk 的默认值定。顺带一提,OFMT 变量可以改 print 的默认数值格式(awk 'BEGIN{OFMT="%.0f"}'),但显式 printf 更直观,出了问题一眼就能在输出语句里看到格式。
同类教训:多文件 awk 的 NR==FNR 陷阱
同一批脚本里还踩过另一个 awk 坑,顺带记一笔。多文件 join 常用 NR==FNR 区分第一个文件:
awk 'NR==FNR { a[$1]=$2; next } { print $1, a[$1] }' map.txt data.txt
如果 map.txt 恰好是空文件,data.txt 的每一行都会满足 NR==FNR,被全部误判成第一个文件,join 结果悄无声息地全灭(2026 年 10 月 30 日 cf-us 分块实踩)。正确做法是用文件名判断归属:
awk 'FILENAME==ARGV[1] { a[$1]=$2; next } { print $1, a[$1] }' map.txt data.txt
另外禁止一边读文件一边往同一文件 append,先写临时文件再合并。
总结
awk print浮点数默认%.6g,大数变科学计数法;sort -n只按前缀解析,排名会错。- 数值进排序/比较前,一律
printf指定格式输出。 - 多文件 awk 用
FILENAME==ARGV[n]而不用NR==FNR判断文件归属,尤其要防第一个文件为空的场景。
- 点赞
- 收藏
- 关注作者
评论(0)