awk 输出的浮点数默认是 %.6g:一次优选 IP 排名全乱的排查

举报
yd_277098636 发表于 2026/11/06 08:19:55 2026/11/06
【摘要】 awk 输出的浮点数默认是 %.6g:一次优选 IP 排名全乱的排查 现象2026 年 10 月 29 日,在做 Cloudflare 优选 IP(新加坡节点)排名时,脚本算出的综合分明明是几千万量级,sort -n 排出来的顺序却全乱了:一个 4.13e+07 分的 IP 排在了 9.8e+06 分的 IP 前面,而按数值算前者应该是后者的 4 倍多。排名结果直接进了当天的优选名单,差点...

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 判断文件归属,尤其要防第一个文件为空的场景。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。