网络三剑客之 awk:Linux 文本数据 “分析神器” 详解(含运维实战)
在 Linux “网络三剑客”(grep、sed、awk)中,awk 是功能最强大、最灵活的 “数据处理工具”—— 核心定位是「按字段分割文本,实现数据提取、统计、计算、格式化输出」,尤其擅长处理结构化文本(如日志、CSV 文件、配置文件)。
如果说 grep 负责 “找行”、sed 负责 “改行”,那么 awk 就负责 “析列”—— 它能轻松分割文本中的字段(比如按空格、逗号、冒号拆分),再对字段进行过滤、计算、统计,是运维日志分析、数据统计、自动化脚本中的 “核心工具”。
本文延续 “大白话 + 可运行实例” 风格,从基础语法到高级实战,帮你彻底掌握 awk,小白也能直接上手!
一、awk 核心定位:什么是 awk?
awk 得名于三位开发者(Aho、Weinberger、Kernighan)的姓氏首字母,核心逻辑:
- 逐行读取文本(从文件或管道输入);
- 按指定 “分隔符”(默认空格 / 制表符)分割当前行,拆分为多个 “字段”(字段 1=$1,字段2=$2,整行 =$0);
- 按 “模式”(可选)匹配行 / 字段;
- 对匹配的行执行 “动作”(打印、计算、统计等);
- 默认不修改原文件,输出处理结果到终端。
简单说:awk 就像一个 “文本数据分析师”,能把杂乱的文本按字段拆分,再进行精准的数据分析和格式化输出,尤其适合处理 “有固定分隔规则” 的文本。
基础语法(3 种常用格式)
bash
运行
# 1. 处理文件:awk [选项] '模式{动作}' 文件名
awk -F : '{print $1,$7}' /etc/passwd # 按冒号分割,打印第1、7字段
# 2. 管道输入:命令输出 | awk [选项] '模式{动作}'(运维最常用)
ifconfig | awk '/inet /{print $2}' # 提取IP地址(第2字段)
# 3. 多行指令:用分号分隔动作,或换行写(适合复杂逻辑)
awk -F , 'BEGIN{print "姓名 年龄"} {print $1,$2} END{print "统计完成"}' data.csv
核心选项(必记)
| 选项 | 核心功能(大白话) | 实战场景 |
|---|---|---|
-F 分隔符 | 指定字段分隔符(默认:空格 / 制表符) | awk -F : '{print $1}' /etc/passwd(按冒号分割) |
-v 变量=值 | 定义自定义变量(传递外部值到 awk) | awk -v num=10 '{print $1+num}' test.txt(字段 1 加 10) |
-f 脚本文件 | 从文件中读取 awk 指令(适合复杂逻辑) | awk -f analysis.awk access.log(从脚本文件执行) |
关键提醒:awk 的指令必须用单引号
' '包裹,避免被 Shell 解析;字段索引从 1 开始($1是第一个字段,$0 是整行)。
二、awk 核心基础:字段、内置变量、模式与动作
要掌握 awk,必须先吃透 “字段分割、内置变量、模式匹配、动作执行” 这四大基础,这是所有高级用法的前提。
1. 字段分割:按规则拆分文本(核心)
awk 默认按「连续的空格 / 制表符」分割字段,也可通过 -F 选项指定任意分隔符(如冒号、逗号、斜杠)。
实例:分割不同格式的文本
场景 1:按冒号分割(/etc/passwd 文件)
/etc/passwd 格式:用户名:密码占位符:UID:GID:描述:家目录:Shell
bash
运行
# 按冒号分割,打印用户名($1)和登录Shell($7),用制表符分隔输出
awk -F : '{print $1 "\t" $7}' /etc/passwd | head -3
输出:
plaintext
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
场景 2:按逗号分割(CSV 文件)
创建测试文件 data.csv:
plaintext
张三,25,北京,工程师
李四,30,上海,产品经理
王五,28,广州,设计师
bash
运行
# 按逗号分割,打印姓名($1)和城市($3)
awk -F , '{print "姓名:" $1 ", 城市:" $3}' data.csv
输出:
plaintext
姓名:张三, 城市:北京
姓名:李四, 城市:上海
姓名:王五, 城市:广州
场景 3:按多字符分隔符分割(如 “-”)
创建测试文件 test.txt:
plaintext
2025-11-21-10:00-INFO-服务启动
2025-11-21-10:05-ERROR-数据库连接超时
bash
运行
# 按“-”分割,打印时间($1-$4拼接)和日志级别($5)
awk -F '-' '{print $1 "-" $2 "-" $3 " " $4 "\t" $5}' test.txt
输出:
plaintext
2025-11-21 10:00 INFO
2025-11-21 10:05 ERROR
2. 内置变量:awk 的 “自带工具包”(必记)
awk 内置了多个实用变量,能直接获取文本的行号、字段数、分隔符等信息,极大简化操作:
| 内置变量 | 核心功能(大白话) | 实例 |
|---|---|---|
$0 | 表示当前整行文本 | awk '/ERROR/{print $0}' test.log(打印包含 ERROR 的整行) |
$n | 表示第 n 个字段(n 为数字,从 1 开始) | awk -F : '{print $3}' /etc/passwd(打印第 3 字段 UID) |
NF | 表示当前行的 “字段总数”(Number of Fields) | awk '{print "字段数:" NF}' test.txt(输出每行字段数) |
NR | 表示当前行的 “行号”(Number of Records) | awk '{print NR ":" $0}' test.txt(每行前加行号) |
FS | 输入字段分隔符(等价于 -F 选项) | awk 'BEGIN{FS=":"} {print $1}' /etc/passwd(BEGIN 中定义分隔符) |
OFS | 输出字段分隔符(默认空格,可自定义) | awk -F : 'BEGIN{OFS="\t"} {print $1,$7}' /etc/passwd(输出用制表符分隔) |
FILENAME | 当前处理的文件名 | awk '{print FILENAME ":" $0}' test.txt(输出文件名 + 行内容) |
实例:用内置变量增强输出
bash
运行
# 处理/etc/passwd,输出:行号 用户名 UID 字段数
awk -F : '{print "行" NR ": 用户名=" $1 ", UID=" $3 ", 字段数=" NF}' /etc/passwd | head -3
输出:
plaintext
行1: 用户名=root, UID=0, 字段数=7
行2: 用户名=bin, UID=1, 字段数=7
行3: 用户名=daemon, UID=2, 字段数=7
3. 模式匹配:精准定位要处理的行 / 字段
awk 的 “模式” 用于筛选行或字段,支持「行匹配」「字段匹配」「范围匹配」,还能结合正则表达式。
常用模式类型
| 模式类型 | 语法 | 功能(大白话) | 实例 |
|---|---|---|---|
| 行号匹配 | n(n 为数字) | 只处理第 n 行 | awk -F : '3{print $1}' /etc/passwd(处理第 3 行) |
| 行范围匹配 | n1,n2 | 处理第 n1 到 n2 行 | awk -F : '1,3{print $1}' /etc/passwd(处理 1-3 行) |
| 正则行匹配 | /正则表达式/ | 处理包含匹配内容的行 | awk '/ERROR/{print $0}' test.log(处理包含 ERROR 的行) |
| 字段匹配 | $n ~ /正则/ | 处理第 n 字段匹配正则的行 | awk -F : '$1 ~ /root/{print $0}' /etc/passwd(用户名含 root 的行) |
| 条件匹配 | 条件表达式 | 处理满足条件的行 | awk -F : '$3>1000{print $1}' /etc/passwd(UID>1000 的普通用户) |
| BEGIN 模式 | BEGIN{动作} | 处理文本前执行(初始化) | awk 'BEGIN{print "开始分析"} {print $0}' test.txt(先输出 “开始分析”) |
| END 模式 | END{动作} | 处理文本后执行(收尾统计) | awk 'END{print "共" NR "行数据"}' test.txt(最后输出总行数) |
实例:模式匹配实战
场景 1:正则行匹配(提取日志中的 ERROR 行)
bash
运行
# 从test.log中提取包含ERROR的行,只打印时间($1-$2)和信息($3-$NF)
awk '/ERROR/{print $1 " " $2 "\t" $3 " " $4}' test.log
输入(test.log):
plaintext
2025-11-21 10:00:00 INFO 服务启动成功
2025-11-21 10:05:00 ERROR 数据库连接超时
2025-11-21 10:10:00 ERROR 端口被占用
输出:
plaintext
2025-11-21 10:05:00 ERROR 数据库连接超时
2025-11-21 10:10:00 ERROR 端口被占用
场景 2:条件匹配(筛选 UID>1000 的普通用户)
bash
运行
# 按冒号分割,筛选UID($3)>1000的行,打印用户名($1)和家目录($6)
awk -F : '$3>1000{print "用户名:" $1 ", 家目录:" $6}' /etc/passwd
输出:
plaintext
用户名:yunwei, 家目录:/home/yunwei
用户名:test, 家目录:/home/test
场景 3:BEGIN+END 模式(统计文件总行数和字段数)
bash
运行
# 处理data.csv,先输出表头,再输出内容,最后统计总行数
awk -F , 'BEGIN{print "姓名\t年龄\t城市"; print "----------------"} {print $1 "\t" $2 "\t" $3} END{print "----------------\n共" NR "条记录"}' data.csv
输出:
plaintext
姓名 年龄 城市
----------------
张三 25 北京
李四 30 上海
王五 28 广州
----------------
共3条记录
4. 动作指令:对字段执行操作(打印、计算、判断)
awk 的 “动作” 是包裹在 { } 中的命令,支持打印、计算、条件判断、循环等,是数据处理的核心。
常用动作指令
| 动作 | 功能(大白话) | 实例 |
|---|---|---|
print 内容 | 打印指定内容(字段、变量、字符串) | awk '{print $1,$3}' test.txt(打印 1、3 字段) |
printf "格式" 内容 | 格式化打印(指定对齐、宽度、类型) | awk -F , '{printf "%-8s %-3d %-6s\n", $1,$2,$3}' data.csv(左对齐) |
| 算术运算 | +、-、*、/、%(字段可直接计算) | awk -F , '{print $1 "年龄+5:" $2+5}' data.csv(年龄加 5) |
| 条件判断 | if(条件){动作}else{动作} | awk -F : '{if($3==0) print $1 "是管理员"; else print $1 "是普通用户"}' /etc/passwd |
| 循环 | for、while(处理批量数据) | awk '{for(i=1;i<=NF;i++) print "字段" i ":" $i}' test.txt(遍历所有字段) |
实例:动作指令实战
场景 1:格式化打印(美化输出)
bash
运行
# 处理data.csv,姓名左对齐占8字符,年龄右对齐占3字符,城市左对齐占6字符
awk -F , '{printf "姓名:%-8s 年龄:%3d 城市:%-6s\n", $1,$2,$3}' data.csv
输出(格式整齐):
plaintext
姓名:张三 年龄: 25 城市:北京
姓名:李四 年龄: 30 城市:上海
姓名:王五 年龄: 28 城市:广州
场景 2:字段计算(统计日志中某字段总和)
创建测试文件 score.txt:
plaintext
小明 85 90 78
小红 92 88 95
小刚 76 80 82
bash
运行
# 计算每个学生的总分和平均分,输出格式化结果
awk '{total=$2+$3+$4; avg=total/3; printf "姓名:%-4s 总分:%3d 平均分:%.1f\n", $1,total,avg}' score.txt
输出:
plaintext
姓名:小明 总分:253 平均分:84.3
姓名:小红 总分:275 平均分:91.7
姓名:小刚 总分:238 平均分:79.3
场景 3:条件判断 + 循环(遍历字段并筛选)
bash
运行
# 处理test.log,遍历所有字段,打印包含“ERROR”或“WARNING”的字段
awk '{for(i=1;i<=NF;i++) if($i ~ /ERROR|WARNING/) print "行" NR "字段" i ":" $i}' test.log
输入(test.log):
plaintext
2025-11-21 10:05:00 ERROR 数据库连接超时
2025-11-21 10:10:00 WARNING 磁盘空间不足
输出:
plaintext
行1字段3:ERROR
行2字段3:WARNING
三、awk 运维高频实战场景(直接复用)
awk 是运维日志分析、数据统计的 “核心工具”,以下场景覆盖 80% 的日常需求,可直接复制使用:
场景 1:Nginx 日志分析 —— 统计访问 Top5 的 IP
假设 Nginx 访问日志 access.log 格式(第 1 字段是 IP,第 7 字段是 URL):
plaintext
192.168.1.100 - - [21/Nov/2025:10:00:00 +0800] "GET / HTTP/1.1" 200 1234
192.168.1.100 - - [21/Nov/2025:10:01:00 +0800] "GET /index.html HTTP/1.1" 200 567
192.168.1.101 - - [21/Nov/2025:10:02:00 +0800] "POST /login HTTP/1.1" 200 890
bash
运行
# 步骤:提取IP($1)→ 排序 → 去重计数 → 按次数反向排序 → 取前5
awk '{print $1}' access.log | sort | uniq -c | sort -nr | head -5
输出(访问次数从多到少):
plaintext
123 192.168.1.100
98 192.168.1.101
67 123.45.67.89
32 192.168.1.102
15 8.8.8.8
场景 2:提取服务器 IP 地址(从 ifconfig/ip addr)
bash
运行
# 方法1:从ifconfig提取(过滤inet开头,排除127.0.0.1)
ifconfig | awk '/inet / && !/127.0.0.1/{print $2}'
# 方法2:从ip addr提取(更通用)
ip addr | awk '/inet / && !/127.0.0.1/{gsub(/\/.*/,"",$2); print $2}'
输出:
plaintext
192.168.1.105
192.168.122.1
注:
gsub(/\/.*/,"",$2)是替换 $2 中 “/” 及后面的内容为空(去除子网掩码,如 192.168.1.105/24→192.168.1.105)。
场景 3:统计系统用户数量(按 UID 分类)
bash
运行
# 统计管理员用户(UID=0)、系统用户(UID1-999)、普通用户(UID≥1000)的数量
awk -F : '
BEGIN{admin=0; system=0; normal=0}
$3==0{admin++}
$3>0 && $3<=999{system++}
$3>=1000{normal++}
END{print "管理员用户:" admin "个"; print "系统用户:" system "个"; print "普通用户:" normal "个"}
' /etc/passwd
输出:
plaintext
管理员用户:1个
系统用户:30个
普通用户:2个
场景 4:日志按级别统计(统计 INFO/ERROR/WARN 的数量)
bash
运行
# 从test.log中统计不同级别日志的数量
awk '
BEGIN{info=0; error=0; warn=0}
$3=="INFO"{info++}
$3=="ERROR"{error++}
$3=="WARN"{warn++}
END{print "INFO:" info "条"; print "ERROR:" error "条"; print "WARN:" warn "条"; print "总计:" info+error+warn "条"}
' test.log
输入(test.log):
plaintext
2025-11-21 10:00:00 INFO 服务启动
2025-11-21 10:05:00 ERROR 数据库超时
2025-11-21 10:10:00 WARN 磁盘空间不足
2025-11-21 10:15:00 INFO 数据备份完成
输出:
plaintext
INFO:2条
ERROR:1条
WARN:1条
总计:4条
场景 5:处理 CSV 文件 —— 筛选并格式化数据
需求:从 user.csv 中筛选 “年龄≥28” 的用户,按 “姓名(左对齐 8 字符)- 年龄(右对齐 3 字符)- 城市” 格式输出:
bash
运行
# user.csv格式:姓名,年龄,城市,职业
awk -F , '$2>=28{printf "%-8s - %3d - %s\n", $1,$2,$3}' user.csv
输入(user.csv):
plaintext
张三,25,北京,工程师
李四,30,上海,产品经理
王五,28,广州,设计师
赵六,35,深圳,架构师
输出:
plaintext
李四 - 30 - 上海
王五 - 28 - 广州
赵六 - 35 - 深圳
场景 6:计算系统负载(提取 uptime 中的 15 分钟负载)
bash
运行
# 从uptime输出中提取15分钟系统负载(第10字段,索引从1开始)
uptime | awk '{print "15分钟系统负载:" $10}'
输出:
plaintext
15分钟系统负载:0.35
四、awk 常见误区与避坑指南
- 字段索引从 1 开始,而非 0:$0是整行,$1 是第一个字段,新手容易误写$0为$1;
- 分隔符包含特殊字符需转义:如果分隔符是
.*/等正则元字符,需用反斜杠转义(如-F '\.'按点号分割 IP); - print 与 printf 的区别:print 自动换行,printf 需手动加
\n;printf 需指定格式符(如%d数字、%s字符串),否则可能输出异常; - 字符串比较需加引号:字段匹配字符串时,字符串需用引号包裹(如
$3=="ERROR",而非$3==ERROR); - 数值比较与字符串比较混淆:
$3>1000是数值比较,$3>"1000"是字符串比较(按字典序),需根据字段类型选择; - BEGIN/END 模式的位置:BEGIN 在处理文本前执行(初始化变量、输出表头),END 在处理后执行(统计结果),不能写在中间;
- 变量赋值无等号空格:awk 中变量赋值是
var=10,而非var = 10(等号两边不能有空格)。
总结:awk 核心要点与三剑客分工
1. awk 核心要点
- 核心定位:按字段分割文本,擅长数据提取、统计、计算、格式化(“析列”);
- 必记基础:字段(\(1-\)n、$0)、内置变量(NR 行号、NF 字段数、FS/OFS 分隔符)、模式(行匹配、条件匹配、BEGIN/END)、动作(print/printf、计算、判断);
- 运维价值:日志分析、数据统计、CSV 处理、系统信息提取,是自动化脚本中的 “数据处理核心”。
2. 网络三剑客分工(清晰定位,避免混淆)
| 工具 | 核心功能 | 适用场景 | 一句话总结 |
|---|---|---|---|
| grep | 按模式匹配行 | 查找特定内容的行(如日志中的 ERROR) | 找行 |
| sed | 按模式修改行 | 批量删除、替换、插入文本(如改配置文件端口) | 改行 |
| awk | 按字段分析数据 | 提取字段、统计计算、格式化输出(如日志 IP 统计) | 析列 |
三者组合使用,能解决几乎所有 Linux 文本处理需求 —— 比如 “grep 找行 → sed 改行 → awk 析列”,或 “awk 提取字段 → sort 排序 → uniq 去重”,是运维效率提升的 “黄金组合”!
836

被折叠的 条评论
为什么被折叠?



