比 Excel 更好用的文本处理神器
上一期讲了 grep,有人说"查日志够用了"。但你有没有遇到过这种情况:日志查到了,但只想看第 3 列和第 5 列,或者想把某个字段求个和?
这时候 grep 就不够用了——它只能帮你找到那一行,但没法帮你"拆"那一行。
能干这活的,叫 awk。
说白了:grep 负责"找",awk 负责"拆"和"算"。
1. 打印指定列:$1 $2 $3
awk 默认按空格拆分每一行,$1 是第一列,$2 是第二列,$0 是整行。
echo "hello world foo" | awk '{print $1, $3}'
# 输出: hello foo想调换顺序也行:{print $3, $1},随便组合。
2. 指定分隔符:-F
不是所有文本都用空格分隔。比如 /etc/passwd 用冒号分隔,CSV 用逗号分隔。
# 用冒号分隔,打印用户名和 shell
awk -F: '{print $1, $7}' /etc/passwd
# 输出: root /bin/bash
# 用逗号分隔
awk -F, '{print $2}' data.csv-F 后面跟分隔符,想用什么就用什么。
3. 条件过滤:只处理符合条件的行
awk 可以加条件,只处理满足条件的行:
# 只打印第一列大于等于 400 的行
echo -e "100 ok\n200 ok\n500 error\n404 notfound" | awk '$1 >= 400 {print}'
# 输出:
# 500 error
# 404 notfound条件写在 {} 前面,支持比较运算符:>、<、>=、<=、==、!=。
4. 内置变量:NR、NF、FS
awk 有一堆好用的内置变量:
# NR = 当前行号
echo -e "line1\nline2\nline3" | awk '{print NR": "$0}'
# 输出:
# 1: line1
# 2: line2
# 3: line3
# NF = 当前行的列数(最后一列是 $NF)
echo "a b c d e" | awk '{print "共"NF"列,最后一列是: "$NF}'
# 输出: 共5列,最后一列是: e常用内置变量速查:
NR = 行号,NF = 列数,FS = 输入分隔符,OFS = 输出分隔符,RS = 行分隔符。
5. BEGIN 和 END:处理前和处理后
BEGIN 在处理所有行之前执行一次,END 在处理完所有行之后执行一次。
echo -e "10\n20\n30" | awk 'BEGIN{print "开始"} {sum+=$1} END{print "总计: "sum}'
# 输出:
# 开始
# 总计: 60这个模式特别适合做统计:在 BEGIN 里初始化变量,在中间累加,在 END 里输出结果。
6. 字符串处理:toupper、substr、length
# 转大写
echo "hello world" | awk '{print toupper($0)}'
# 输出: HELLO WORLD
# 截取子串(从第2个字符开始,取3个)
echo "abcdefgh" | awk '{print substr($0, 2, 3)}'
# 输出: bcd
# 字符串长度
echo "hello" | awk '{print length($0)}'
# 输出: 57. 格式化输出:printf
print 是随便打,printf 能控制格式:
echo -e "alice 90\nbob 85\ncharlie 95" | awk '{printf "%-10s %3d分\n", $1, $2}'
# 输出:
# alice 90分
# bob 85分
# charlie 95分%-10s = 左对齐、占 10 个字符宽,%3d = 右对齐、占 3 位数字。跟 C 语言的 printf 一模一样。
8. 多条件组合:&&、||
# 第3列是 A 且第2列大于 90
echo -e "alice 90 A\nbob 85 A\ncharlie 95 A" | awk '$3=="A" && $2>90 {print $1}'
# 输出: charlie
# 第1列是 error 或者第1列是 warning
awk '$1=="error" || $1=="warning"' /var/log/app.log9. 替换内容:gsub 和 sub
# gsub = 全局替换(所有匹配都换)
echo "foo bar foo baz foo" | awk '{gsub(/foo/, "FOO"); print}'
# 输出: FOO bar FOO baz FOO
# sub = 只替换第一个匹配
echo "foo bar foo baz" | awk '{sub(/foo/, "FOO"); print}'
# 输出: FOO bar foo baz注意:gsub 和 sub 会修改 $0,如果要替换某一列,用 gsub(/pattern/, "replacement", $3)。
10. 实用统计:求和、平均、最大值
# 求和 + 平均
echo -e "10\n20\n30\n40" | awk '{sum+=$1; count++} END{print "总和: "sum, "平均: "sum/count}'
# 输出: 总和: 100 平均: 25
# 最大值
echo -e "10\n5\n30\n20" | awk 'max=="" || $1>max {max=$1} END{print "最大值: "max}'
# 输出: 最大值: 30
# 统计日志中各状态码出现次数
awk '{status[$9]++} END{for(s in status) print s, status[s]}' /var/log/nginx/access.log速查表
| 场景 |
命令 |
| 打印第1、3列 |
awk '{print $1, $3}' |
| 指定分隔符 |
awk -F: '{print $1}' |
| 条件过滤 |
awk '$1>100 {print}' |
| 打印行号 |
awk '{print NR, $0}' |
| 求和 |
awk '{sum+=$1} END{print sum}' |
| 平均值 |
awk '{s+=$1;c++} END{print s/c}' |
| 全局替换 |
awk '{gsub(/old/,"new"); print}' |
| 格式化输出 |
awk '{printf "%-10s %3d\n",$1,$2}' |
| 去重(保留顺序) |
awk '!seen[$0]++' file |
| 统计词频 |
awk '{for(i=1;i<=NF;i++) w[$i]++} END{for(k in w) print k,w[k]}' |
一句话总结
grep 负责"找",awk 负责"拆"和"算"。两个配合起来,日志分析基本够用。
下期预告:sed —— 不用打开文件就能改内容的神器
—— 三页札记 ——
关注我,每周分享 Linux 运维实战技巧
