ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

awk命令从入门到精通:字段、模式、动作、内置变量实战

awk命令从入门到精通:字段、模式、动作、内置变量实战 awk 是三剑客里最像编程语言的一个。grep 和 sed 是给个规则跑一遍awk 则允许你写条件、循环、数组把每行按字段拆开做计算。日志统计、CSV 汇总、配置抽取这些活awk 写出来的脚本经常比 Python 还短。这一篇把字段、内置变量、BEGIN/END、数组统计这几块核心用法讲清楚。一、awk 程序的三段结构一个完整的 awk 程序长这样BEGIN { 开始前执行一次 } 模式 { 命中每一行时执行 } END { 读完所有行后执行一次 }三段都是可选的。最常见的形态是只写中间那段模式动作awk -F, $3 研发部 { print $2 } employees.csv-F,告诉 awk 用逗号切字段。$3 研发部是模式条件。{ print $2 }是动作。awk 对每一行依次做模式成立 → 执行动作模式不成立 → 跳过。二、字段$0、$1 到 $NF这是 awk 最核心的概念必须先搞懂。沿用employees.csv101,张三,研发部,北京,18000 102,李四,研发部,上海,22000 103,王五,产品部,北京,25000用-F,切完以后每行内部的字段变量是变量含义当前行举例$0当前整行原文101,张三,研发部,北京,18000$1第 1 个字段101$2第 2 个字段张三$3第 3 个字段研发部$4第 4 个字段北京$5第 5 个字段18000NF当前行字段总数number of fields5$NF最后一个字段等于$518000$(NF-1)倒数第二个字段北京注意区分$后面跟数字才是字段NF前面没有$是个数字。这是新手第一周必错的点。示例只打印姓名和月薪awk -F, { print $2, $5 } employees.csv预期输出张三 18000 李四 22000 王五 25000print后面多个参数之间默认用空格连接这是输出分隔符 OFS后面讲。示例带条件过滤打印月薪大于等于 22000 的人awk -F, $5 22000 { print $2, $5 } employees.csv预期输出李四 22000 王五 25000数值比较直接写就行awk 会自动把字符串形式的数字当数字处理。三、常用内置变量变量含义默认值FS输入字段分隔符任意空白空格/TabOFS输出字段分隔符空格RS输入记录分隔符默认是换行\nORS输出记录分隔符\nNF当前行字段数—NR当前已读的总记录数跨文件累加—FNR当前文件内的记录号多个文件时每个文件从 1 重新算—FILENAME当前正在处理的文件名—CONVFMT数字转字符串格式%.6gNR和FNR的区别在多文件场景才看得出来awk ... a.csv b.csv时处理到 b.csv 第 1 行NR是 a 行数1FNR是 1。示例用 NR 跳表头如果 CSV 第一行是表头id,name,dept,city,salary 101,张三,研发部,北京,18000 102,李四,研发部,上海,22000统计月薪时要把表头排除awk -F, NR 1 { sum $5 } END { print sum } employees.csv示例用 OFS 改输出分隔符想把结果用逗号分隔输出awk -F, BEGIN { OFS, } { print $2, $4, $5 } employees.csv预期输出张三,北京,18000 李四,上海,22000 王五,北京,25000BEGIN块在处理任何行之前跑一次专门用来初始化变量。四、BEGIN 和 END脚本的开头和收尾BEGIN 块在读取任何输入之前执行一次。用来设置FS、OFS或者打印表头。也可以直接在 BEGIN 里设 FS等价于命令行-F,awk BEGIN { FS,; OFS | } { print $1, $2 } employees.csvEND 块读完所有行后执行一次。用来输出累加结果、总结。前面算北京员工月薪总和的例子awk -F, $4 北京 { sum $5; n } END { print 北京共, n, 人, 月薪合计, sum } employees.csv预期输出北京共 2 人, 月薪合计 43000n是顺便数人数。这种边读边累加读完一次性出结论的结构是 awk 最擅长的事。五、print 与 printfprint简单输出print $1, $2 # 用 OFS 连接 print $1 $2 # 直接拼接中间没分隔 print # 空行printf格式化输出想控制对齐、补零、保留几位小数用printf语法和 C 语言一模一样awk -F, { printf %-6s %-6s %8d\n, $2, $4, $5 } employees.csv预期输出张三 北京 18000 李四 上海 22000 王五 北京 25000%-6s表示字符串左对齐占 6 位%8d表示整数右对齐占 8 位。做报表的时候 printf 比 print 整齐得多。六、条件、循环和关联数组awk 不是只能干过滤它是个完整的脚本语言。if 条件awk -F, { if ($5 22000) print $2, 高薪; else print $2, 普通 } employees.csv数组关联数组统计各部门月薪总和awk 的数组是关联数组下标可以是字符串awk -F, { sum[$3] $5 } END { for (d in sum) print d, sum[d] } employees.csv预期输出顺序不固定研发部 40000 产品部 25000这一行脚本干了三件事读每行把第 5 列累加进sum[部门名]。遇到新部门数组自动新建一个 key。读完后遍历所有 key 打印。按部门统计人数、按城市统计访问量、按状态码分组都是这个套路。循环求前 5 行每行字段数awk NR 5 { for (i1; iNF; i) print NR, i, $i } /etc/passwd | head -10七、实战把 Nginx 默认日志按状态码统计准备一个迷你access.log192.168.1.10 - - [16/Sep/2026:10:23:45 0800] GET /api/user HTTP/1.1 200 1234 192.168.1.11 - - [16/Sep/2026:10:23:46 0800] POST /api/login HTTP/1.1 200 89 192.168.1.10 - - [16/Sep/2026:10:23:47 0800] GET /api/order HTTP/1.1 500 0 192.168.1.12 - - [16/Sep/2026:10:23:48 0800] GET /static/app.js HTTP/1.1 200 45211 192.168.1.10 - - [16/Sep/2026:10:23:49 0800] DELETE /api/user/3 HTTP/1.1 403 21默认按空格切字段状态码在第 9 列awk { code[$9] } END { for (c in code) print c, code[c] } access.log预期输出顺序不固定200 3 500 1 403 1再把流量加起来第 10 列是字节数awk { sum $10 } END { printf 总流量 %.2f MB\n, sum/1024/1024 } access.log预期输出总流量 0.05 MB八、⚠️ 常见错误-F漏写或写错。CSV 用逗号Nginx 日志用空格/etc/passwd用冒号。不写-F默认按空白切遇到冒号分隔的文件切出来全是错的。$NF和NF混用。NF是数字$NF是最后一个字段。写print NF打印字段数写print $NF打印内容。字符串比较和数字比较搞混。$5 22000是数字比较$5 22000在 awk 里结果差不多但$5 0022这种带前导零的写法要小心。多文件时 NR 不对。想每个文件从 1 开始数行号用FNR别用NR。数组遍历顺序随机。for (i in arr)的顺序在 awk 里不保证想排序要接sort或者用 GNU awk 的PROCINFO[sorted_in]。在 shell 里用双引号包 awk 脚本。awk { print $1 }会被 shell 先把$1替换成 shell 的位置参数结果 awk 拿到的是空。awk 脚本一定用单引号。九、原理探究awk 是怎么把一行切成字段的awk 的字段切分不是简单地按分隔符 split。默认行为有点讲究不写-F时awk 把任意连续的空白空格、Tab都当成一个分隔符并且忽略行首行尾的空白。这就是为什么默认情况下ls -l | awk { print $5 }能直接拿到文件大小——不管ls -l输出里空格多长$5永远对。写了-F,或FS,时分隔符变成单个逗号连续两个逗号会被当成空字段行首行尾的分隔符也会产生空字段。这个差异实际影响很大。看这个例子101,,研发部,北京,18000默认空白切分$1101、$2研发部——中间的空字段直接被吞了。-F,切分$1101、$2、$3研发部——空字段被保留。做 CSV 解析时一定要用-F,否则缺数据的行会悄悄错位。另外GNU awk 还支持用正则做 FS比如FS[,;]表示逗号或分号都算分隔符。但生产环境写这种脚本要小心数据格式一变化字段数就对不上。最后提一句awk 有三个主要实现——nawk新版 awk、gawkGNU awk功能最全Linux 默认、mawk快但功能少。日常脚本用gawk写发布到极简容器前最好用mawk或 BusyBox awk 跑一遍免得用了 GNU 扩展别的环境不认。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进