网络三剑客——awk

网络三剑客之 awk:Linux 文本数据 “分析神器” 详解(含运维实战)

在 Linux “网络三剑客”(grep、sed、awk)中,awk 是功能最强大、最灵活的 “数据处理工具”—— 核心定位是「按字段分割文本,实现数据提取、统计、计算、格式化输出」,尤其擅长处理结构化文本(如日志、CSV 文件、配置文件)。

如果说 grep 负责 “找行”、sed 负责 “改行”,那么 awk 就负责 “析列”—— 它能轻松分割文本中的字段(比如按空格、逗号、冒号拆分),再对字段进行过滤、计算、统计,是运维日志分析、数据统计、自动化脚本中的 “核心工具”。

本文延续 “大白话 + 可运行实例” 风格,从基础语法到高级实战,帮你彻底掌握 awk,小白也能直接上手!

一、awk 核心定位:什么是 awk?

awk 得名于三位开发者(Aho、Weinberger、Kernighan)的姓氏首字母,核心逻辑:

  1. 逐行读取文本(从文件或管道输入);
  2. 按指定 “分隔符”(默认空格 / 制表符)分割当前行,拆分为多个 “字段”(字段 1=$1,字段2=$2,整行 =$0);
  3. 按 “模式”(可选)匹配行 / 字段;
  4. 对匹配的行执行 “动作”(打印、计算、统计等);
  5. 默认不修改原文件,输出处理结果到终端。

简单说: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. 字段索引从 1 开始,而非 0:$0是整行,$1 是第一个字段,新手容易误写$0为$1;
  2. 分隔符包含特殊字符需转义:如果分隔符是. * / 等正则元字符,需用反斜杠转义(如 -F '\.' 按点号分割 IP);
  3. print 与 printf 的区别:print 自动换行,printf 需手动加\n;printf 需指定格式符(如%d数字、%s字符串),否则可能输出异常;
  4. 字符串比较需加引号:字段匹配字符串时,字符串需用引号包裹(如 $3=="ERROR",而非 $3==ERROR);
  5. 数值比较与字符串比较混淆$3>1000 是数值比较,$3>"1000" 是字符串比较(按字典序),需根据字段类型选择;
  6. BEGIN/END 模式的位置:BEGIN 在处理文本前执行(初始化变量、输出表头),END 在处理后执行(统计结果),不能写在中间;
  7. 变量赋值无等号空格: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 去重”,是运维效率提升的 “黄金组合”!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值