跳到主要内容

用 awk 读列、用 sed 修行的 CSV 工作流

面向中文开发者的 awk 与 sed CSV 实战指南:真实输入输出、本机 benchmark、常见边界和 Toolora 速查链接。

发布于 作者 李雷
#awk #sed #csv #developer #cheatsheet

zh blog for awk-sed-cheatsheet:用 awk 读列、用 sed 修行的 CSV 工作流

很多 CSV 小任务不需要打开 Excel,也不需要写一段 Python。订单导出、埋点汇总、日志抽样、运营给的临时表,只要字段干净,一条 awksed 就能得到答案。我要查语法时会先打开 awk-sed-cheatsheet,但真正决定效率的不是背多少命令,而是先判断:这一步是在“按列计算”,还是在“按行修补”。

这篇只讲一个角度:CSV 列处理。它和普通日志处理不一样,CSV 看起来是文本,实际又带一点表格结构。awk 很适合求和、分组、筛选;sed 更适合把分隔符、空白、固定前缀这种行级问题先清理掉。遇到需要浏览器里直接转换的数据,我通常把 CSV 转 JSON 放在旁边;遇到正则拿不准的地方,再查 Regex 速查表

先看边界:干净 CSV 才适合一行命令

awk -F, 的意思是用逗号切列。这个写法对很多导出文件都够用:每一行字段数量固定,字段里不再出现未转义逗号,表头只有一行。比如 order_id,channel,amount,status 这种文件,用 awk 处理很直接。

但如果 CSV 里有 "上海,浦东" 这样的带引号逗号,普通 awk -F, 会把它拆成两列。这个时候不要硬写复杂正则,应该换能理解 CSV 引号规则的工具,或者先用浏览器里的 CSV 转 JSON 检查结构。awk 的优势是快、短、贴近终端;它不是通用 CSV parser。

sed 的边界也要说清楚。它不理解“第 3 列”,但它很适合做机械修补:把 Windows 换行里的 \r 去掉,把分号分隔改成逗号,把固定前缀删掉,把 staging.example.com 换成 api.example.com。只要你的规则是“每一行都按同样的文本模式改”,先考虑 sed

真实输入输出:按渠道汇总已支付金额

假设有一个真实可运行的小文件 orders.csv

order_id,channel,amount,status
T1001,wechat,129.50,paid
T1002,alipay,89.00,paid
T1003,wechat,42.90,refunded
T1004,douyin,218.00,paid
T1005,alipay,35.50,paid
T1006,wechat,76.00,paid

现在只统计 paid 订单,并按渠道汇总金额:

awk -F, 'NR>1 && $4=="paid" {
  sum[$2] += $3
}
END {
  for (channel in sum) printf "%s %.2f\n", channel, sum[channel]
}' orders.csv | sort

实际输出是:

alipay 124.50
douyin 218.00
wechat 205.50

这段命令里,NR>1 跳过表头,$4=="paid" 只保留已支付行,sum[$2] += $3 按渠道累加金额。最后接 sort 不是为了计算,而是让输出顺序稳定,方便复制到工单或发给同事。

如果我还想看退款订单,可以换成条件筛选:

awk -F, 'NR>1 && $4=="refunded" { print $1 "," $2 "," $3 }' orders.csv

输出:

T1003,wechat,42.90

这个例子里没有伪输出,复制文件和命令就能复现。它也是我最常用的 awk 场景:先跳表头,再按某一列过滤,然后对另一列求和或打印。

sed 先修行,awk 再算列

很多临时 CSV 不是标准逗号分隔,而是从老系统里导出的分号分隔。比如:

order_id;channel;amount;status
T2001;wechat;55.00;paid
T2002;alipay;31.00;paid

如果字段本身不含分号,先用 sed 改成逗号很干净:

sed 's/;/,/g' legacy-orders.csv

实际输出:

order_id,channel,amount,status
T2001,wechat,55.00,paid
T2002,alipay,31.00,paid

也可以不改文件,直接管道给 awk

sed 's/;/,/g' legacy-orders.csv |
awk -F, 'NR>1 { total += $3 } END { printf "%.2f\n", total }'

输出:

86.00

我写这种管道时有一个习惯:前半段只做格式修补,后半段只做字段计算。不要让一条命令同时承担太多含义。半年后回头看,sed 负责“行长什么样”,awk 负责“列怎么算”,读起来更清楚。

我测了一次:简单替换用 sed 更轻

我在 2026-06-02 做了一次本机 benchmark,平台是 Darwin 25.2.0 arm64,命令位置是 /usr/bin/sed/usr/bin/awkawk version 20200816。测试文件有 300,000 行,内容形如 order_1,team-a,1。每个命令跑 5 次,用 /usr/bin/time -preal 时间。

测试命令是:

sed 's/team-a/team-b/' toolora_awk_sed_bench.csv > /dev/null
awk '{sub(/team-a/,"team-b"); print}' toolora_awk_sed_bench.csv > /dev/null

5 次结果里,sed0.08, 0.08, 0.09, 0.08, 0.08 秒,中位数 0.08 秒;awk0.20, 0.19, 0.20, 0.20, 0.19 秒,中位数 0.20 秒。也就是说,在这个简单替换 benchmark 里,sed 的中位耗时比 awk 少 60%(来源:本文 2026-06-02 本机 benchmark,命令如上)。

这个数字只说明一个窄场景:纯替换时,sed 更轻。它不表示 sed 在所有任务里都更快。只要问题变成“看第几列”“按渠道分组”“求平均值”,awk 的表达能力会把那点时间差赚回来。工具选择应该跟任务形状绑定,而不是跟习惯绑定。

交付前的检查清单

第一,先在小样本上跑。用 head -20 orders.csv 或复制 3 行测试,不要一上来就对全量文件执行 sed -i。macOS 和 GNU sed 的 -i 写法不同,直接写回文件前尤其要小心。

第二,表头要显式处理。NR>1 看起来只是 4 个字符,但它能避免把 amount 当数字参与计算。遇到无表头文件,也建议在命令旁边写清楚第几列是什么。

第三,输出要稳定。分组后的 awk 数组遍历顺序不保证固定,给结果接一个 sort,对比和复查都会省事。要和修改前结果比对时,可以把输出送进 文本差异对比

第四,正则不要逞强。sed 's/foo/bar/' 很可靠;一旦你开始写很长的分组、回溯和转义,先去 Regex 速查表 确认语法,再决定这是不是应该交给脚本语言。

我自己的判断很简单:按行修补,用 sed;按列计算,用 awk;需要确认语法时,打开 awk-sed-cheatsheet;需要处理带引号、嵌套或跨格式转换的数据,就换更懂结构的工具。这样写出来的命令不一定最短,但更容易复查,也更不容易把临时任务变成线上事故。


Made by Toolora · Updated 2026-06-02