【Linux从入门到镜头】第29篇:文本处理三剑客(下)——awk 数据处理神器

目录

一、引言:从"按行处理"到"按字段处理"

[二、awk基础:pattern + action](#二、awk基础:pattern + action)

[2.1 基本结构](#2.1 基本结构)

[2.2 最简单的例子](#2.2 最简单的例子)

[2.3 字段与分隔符](#2.3 字段与分隔符)

[2.4 pattern的多种写法](#2.4 pattern的多种写法)

三、核心内置变量

NR与NF的差异快速理解

四、实战一:Nginx日志分析

[4.1 统计每个IP的访问次数](#4.1 统计每个IP的访问次数)

[4.2 统计状态码分布](#4.2 统计状态码分布)

[4.3 计算平均响应时间](#4.3 计算平均响应时间)

五、实战二:流量统计

六、实战三:成绩统计

[七、awk Mini手册](#七、awk Mini手册)

八、awk一句话速查

九、本篇小结

动手练习

十、下篇预告


一、引言:从"按行处理"到"按字段处理"

回顾三剑客的定位:

工具 核心能力 典型场景
grep 过滤 找到包含error的日志行
sed 编辑 批量替换配置文件中的IP
awk 字段处理 统计每个IP的访问次数、计算平均响应时间

grep和sed眼中只有"一行一行"的文本,awk的独特之处在于:它能把每一行拆成多个字段(默认用空白分隔),然后对字段进行数学运算、条件过滤、格式化输出。这使得awk在日志分析、数据统计、报表生成等场景下无可替代。

二、awk基础:pattern + action

2.1 基本结构

bash

复制代码
awk 'pattern { action }' 文件名
  • pattern :条件(什么行需要处理)。省略时,处理所有行

  • action :动作(对符合条件的行做什么)。省略时,默认动作是print(打印整行)

执行逻辑:awk逐行读取文件,对每一行,先判断pattern是否成立,如果成立则执行花括号中的action。

2.2 最简单的例子

bash

复制代码
# 打印文件所有内容(没有pattern,默认匹配所有行;没有action,默认print)
awk '{ print }' file.txt

# 等价于
awk '1' file.txt          # pattern为1(恒真),action省略→打印

2.3 字段与分隔符

awk的核心抽象:每一行由多个字段 组成,字段默认由连续的空白字符(空格或Tab)分隔。

变量 含义
$0 整行内容
$1 第1个字段
$2 第2个字段
$NF 最后一个字段
NF 当前行的字段数量

bash

复制代码
# 以 /etc/passwd 为例(冒号分隔),先看原始内容
head -3 /etc/passwd

# 打印第1个和第7个字段(默认空白分隔,passwd实际是冒号,这里仅演示)
echo "root x 0 0 root /root /bin/bash" | awk '{ print $1, $7 }'
# 输出:root /bin/bash

指定分隔符

bash

复制代码
# 用 -F 指定分隔符为冒号
awk -F: '{ print $1, $7 }' /etc/passwd

# 分隔符可以是正则
awk -F'[: ]' '{ print $1 }' file   # 冒号或空格都算分隔符

2.4 pattern的多种写法

按条件过滤,下面几个例子逐步递进:

bash

复制代码
# 打印第三字段大于1000的行
awk -F: '$3 > 1000 { print }' /etc/passwd

# 打印包含root的行
awk '/root/ { print }' /etc/passwd

# 复合条件:第三字段大于1000 且 包含bash
awk -F: '$3 > 1000 && /bash/ { print }' /etc/passwd

BEGIN与END块------这是awk非常重要的特殊pattern:

  • BEGIN { }:在处理第一行之前执行一次,通常用于打印表头或初始化变量

  • END { }:在处理完所有行之后执行一次,通常用于输出汇总结果

bash

复制代码
awk -F: 'BEGIN { print "用户名\tUID"; print "-------\t---" }
         { print $1 "\t" $3 }
         END   { print "-------\t---"; print "共 " NR " 个用户" }' /etc/passwd

这个例子完整展示了:先打表头(BEGIN),逐行处理数据(中间块),最后汇总(END)。三个块分工明确,是awk脚本最常用的结构。

三、核心内置变量

变量 含义 典型用法
NR 当前处理的是第几行(Number of Record) NR==1 跳过首行,END { print NR } 统计总行数
NF 当前行的字段数(Number of Fields) $NF 取最后一个字段,NF>5 过滤字段数
$0 整行原始内容 print $0
$1, $2... 第1、2...个字段 print $1, $3
FS 输入字段分隔符(Field Separator) 等同于-F选项
OFS 输出字段分隔符(Output Field Separator) -v OFS=',' 输出CSV格式
RS 输入记录分隔符(默认换行符) RS='' 按段落读

NRNF的差异快速理解

  • NF告诉你"这一行 有几个字段",是每行不同的属性

  • NR告诉你"现在读到了第几行 ",是累计递增的计数器

bash

复制代码
# 打印行号和每行字段数,直观看到两者的区别
awk '{ print "行号:" NR, "字段数:" NF }' /etc/passwd | head -3
# 行号:1 字段数:1
# 行号:2 字段数:1
# 行号:3 字段数:1
# (因为passwd默认没有空格,整行被当成一个字段,下一节用-F:就能看到变化)

四、实战一:Nginx日志分析

4.1 统计每个IP的访问次数

bash

复制代码
awk '{ ip[$1]++ } END { for (i in ip) print ip[i], i }' /var/log/nginx/access.log | sort -rn | head

逐层拆解

  • { ip[$1]++ }:对每一行,把第一个字段(IP地址)作为数组ip的键,值加1。这是awk特有的关联数组用法,不需要预先声明

  • END { for (i in ip) print ip[i], i }:处理完所有行后,遍历数组,打印每个IP和它的出现次数

4.2 统计状态码分布

bash

复制代码
awk '{ status[$9]++ } END { for (code in status) print code, status[code] }' /var/log/nginx/access.log | sort -k2 -rn

$9是Nginx日志中状态码所在的字段(具体字段编号取决于日志格式,常见配置中第9字段是状态码)。

4.3 计算平均响应时间

bash

复制代码
# 假设最后一列是响应时间(微秒)
awk '{ sum+=$NF; count++ } END { if (count>0) printf "平均响应时间: %.2f ms\n", sum/count/1000 }' access.log

解释sum+=$NF对每行最后一个字段累加,count++统计行数。END块计算平均值,printf格式化保留两位小数。除以1000是因为这里假设原始值是微秒。

五、实战二:流量统计

假设有一个流量日志traffic.log,格式为:时间 设备名 入站流量(字节) 出站流量(字节)

text

复制代码
09:00 eth0 1024000 2048000
09:00 eth1 512000 1024000
09:05 eth0 2048000 3072000
09:05 eth1 768000 1536000

按设备统计总流量(入站+出站)

bash

复制代码
awk '{
    total[$2] += ($3 + $4)
}
END {
    print "设备\t总流量(MB)"
    for (dev in total) printf "%s\t%.2f\n", dev, total[dev]/1024/1024
}' traffic.log

逐行累加每个设备的流量(字节),最后转换成MB并输出。total[$2]以设备名称为键的数组,+=把当前行的入站和出站字节累加到该设备名下。

六、实战三:成绩统计

假设有成绩文件scores.txt

text

复制代码
张三 85 92 78
李四 90 88 95
王五 76 84 80

计算每个人的总分和平均分

bash

复制代码
awk '{
    total = $2 + $3 + $4
    avg = total / 3
    printf "%-10s 总分: %3d  平均: %.1f\n", $1, total, avg
}' scores.txt

输出所有学生的单科平均(各列平均值)

bash

复制代码
awk '{
    sum2 += $2; sum3 += $3; sum4 += $4
}
END {
    printf "科目1平均: %.1f\n", sum2/NR
    printf "科目2平均: %.1f\n", sum3/NR
    printf "科目3平均: %.1f\n", sum4/NR
}' scores.txt

这里对每行的三个科目分别累加,在END块中除以总人数(NR)得到平均值。

七、awk Mini手册

命令结构awk [options] 'pattern { action }' file

常用选项

选项 作用
-F: 设置输入分隔符
-v var=value 传入外部变量
-f script.awk 从文件读取awk脚本

常用内置变量

变量 含义
$0 整行
$1...$N 第1到N个字段
NF 字段数
NR 行号
FS/OFS 输入/输出分隔符

常用运算符+ - * / %(算术)、> < >= <= == !=(比较)、&& || !(逻辑)、~ !~(正则匹配/不匹配)

常用函数print(输出)、printf(格式化输出)、length()(字符串长度)、substr()(子串)、split()(字符串拆分成数组)、system()(调用系统命令)

printf常用格式%s字符串、%d整数、%f浮点、%.2f保留两位小数、%-10s左对齐占10字符

八、awk一句话速查

bash

复制代码
# 统计文件总行数
awk 'END { print NR }' file

# 打印第5行
awk 'NR==5' file

# 打印每行最后一个字段
awk '{ print $NF }' file

# 打印字段数大于5的行
awk 'NF > 5' file

# 按字段去重(只输出第一次出现的$1)
awk '!seen[$1]++' file

# 数值求和
awk '{ sum+=$1 } END { print sum }' file

# 数值求平均
awk '{ sum+=$1; count++ } END { print sum/count }' file

# 查找最大值
awk 'max < $1 { max=$1 } END { print max }' file

九、本篇小结

三剑客分工总结

工具 擅长 核心思维
grep 文本过滤(按行找东西) 这一行要不要
sed 文本编辑(按行改东西) 这一行怎么改
awk 数据分析(按字段算东西) 这些字段怎么算

awk核心语法

text

复制代码
awk [选项] 'BEGIN { 初始化 } pattern { 逐行处理 } END { 输出汇总 }' 文件
  • 字段$1$2... $NF(最后一个字段)、$0(整行)

  • 变量NR(行号)、NF(字段数)

  • 数组 :关联数组,键可以是字符串,如count[$1]++

  • 输出print(普通输出)、printf(格式化输出)

记忆口诀

grep找行,sed改行,awk分列算总账。

动手练习

bash

复制代码
# 1. 统计/etc/passwd中每种Shell的用户数量
awk -F: '{ shells[$NF]++ } END { for (s in shells) print shells[s], s }' /etc/passwd

# 2. 计算当前目录下所有文件大小的总和
ls -l | awk '{ sum+=$5 } END { printf "总大小: %.2f MB\n", sum/1024/1024 }'

# 3. 查找系统中UID最大的用户(利用关联数组记录最大值对应的用户名)
awk -F: 'max < $3 { max=$3; user=$1 } END { printf "最大UID用户: %s (UID: %d)\n", user, max }' /etc/passwd

# 4. 将CSV格式的冒号分隔数据转为逗号分隔
awk 'BEGIN { FS=":"; OFS="," } { $1=$1; print }' /etc/passwd | head -3

第4题说明$1=$1这个看起来很怪的操作,是"通知awk重新用OFS组装字段"的惯用技巧。如果不改任何字段值,awk默认不会触发OFS重新拼接。

十、下篇预告

三剑客系列到这里就完整了。但学了这么多命令,真正的价值在于把它们组合成自动化方案

下一篇,我们将进行Shell脚本系列的综合实战 ------编写一个功能完备的Linux系统体检脚本。这个脚本会整合前面学过的变量、条件判断、循环、函数、文本处理等所有知识,一键输出CPU负载、内存使用率、磁盘使用率、网络连接数等关键指标,并以彩色高亮的方式展示出来。这将是你Shell脚本学习成果的一次全面检验。


延伸思考:awk其实是一门完整的编程语言,有变量、数组、循环、条件判断、函数。本文只覆盖了最常用的20%功能,足以应对80%的日常场景。如果遇到更复杂的数据处理需求(如多文件关联、复杂格式解析),可以查阅awk的完整文档,或者学习Python等现代脚本语言作为替代。但在命令行下,"一行awk命令解决一个问题"的高效体验,是Python脚本无法替代的。

相关推荐
xyx-3v1 小时前
信号量(二进制/计数)
java·linux·数据库
炘爚1 小时前
Linux(整理合集)
linux
网络安全许木1 小时前
自学渗透测试第28天(协议补漏与FTP抓包)
运维·服务器·网络安全·渗透测试·php
JiaWen技术圈1 小时前
nftables 添加规则时支持的匹配条件与语句全解
linux·服务器
V我五十买鸡腿1 小时前
网安基础 Windows 和 Linux 那些常用命令
linux·运维·windows
ShineWinsu1 小时前
对于Linux:进程间通信IPC(匿名管道)的解析
linux·c++·面试·进程·通信·管道·ipc
日取其半万世不竭2 小时前
用云服务器部署 Hexo 博客,Nginx 托管静态页面全流程
运维·服务器·nginx
handler012 小时前
进程状态流转的本质:Linux 内核队列与底层数据结构解密
linux·运维·c语言·数据结构·c++·笔记·学习
freshman_y2 小时前
Linux开发中DTS和/proc/device-tree讲解
linux·嵌入式