Linux基础及应用教程:文本三剑客之awk
一、实验目标
- 理解
awk的基本工作方式(逐行读取、按字段处理)。 - 掌握
awk的基本使用:打印整行、打印指定字段、设置分隔符。 - 学会使用简单条件(行号、字段值、模式匹配)筛选要处理的行。
- 认识常见内置变量(如
NR、NF、FS、OFS等)的含义和用法。 - 能够使用
BEGIN/END块实现简单的格式控制与统计。 - 通过对配置文件与系统用户信息的练习,体验
awk在日常文本处理和运维中的应用。
二、实验环境
- 实验平台:VMware 虚拟机
- 操作系统:CentOS 7
- 登录用户:
njucm - 普通用户密码:
123456 - root 密码:
123456
三、实验内容与步骤
文件准备(创建实验环境)
本实验继续使用 自建测试文本 和 系统自带配置文件的副本 进行练习,方便同时比较 sed 与 awk 的不同用法。
# 1. 创建实验目录
mkdir -p ~/awk_lab
cd ~/awk_lab
# 2. 准备一个多行测试文本文件(与 sed 实验类似)
cat > poem.txt << 'EOF'
The quick brown fox jumps over the lazy dog.
The quick brown fox likes Linux.
linux is powerful and flexible.
AWK is a text processing tool.
Awk can handle fields and patterns easily.
EOF
# 3. 准备一个带有简单配置格式的文件
cat > config.txt << 'EOF'
# Sample configuration file
HOST=localhost
PORT=8080
USER=admin
PASS=123456
LOG_LEVEL=info
EOF
# 4. 复制系统自带文本文件作为练习(只在自己的家目录操作)
cp /etc/passwd passwd.sample
# 5. 准备一个简单数字文件,用于统计示例
cat > numbers.txt << 'EOF'
10
20
30
40
50
EOF
# 6. 再准备一个日志模拟文件
cat > app.log << 'EOF'
[2025-01-01 10:00:00] INFO Service started on port 8080
[2025-01-01 10:05:00] WARN High memory usage detected
[2025-01-01 10:10:00] ERROR Failed to connect to database
[2025-01-01 10:15:00] INFO Service is running
EOF
之后的所有 awk 练习命令均在 ~/awk_lab 目录中完成。
任务1:awk命令基础使用
本任务让你体会 awk 的基本行为:逐行读取、按字段拆分,并对每行执行 { 动作 }。
-
查看文件内容:打印整行(
$0)# 使用 awk 打印整行(等价于 cat) awk '{print}' poem.txt # 明确写出 $0,表示“当前整行” awk '{print $0}' poem.txt说明:
$0表示当前整行内容,是 awk 默认处理的对象。 -
打印行号:
NR# 在每行前面加上行号(NR) awk '{print NR, $0}' poem.txtNR是 awk 的内置变量,表示当前处理的是“第几行”。 -
打印指定行(用 NR 做简单条件)
# 打印第 1 行 awk 'NR==1 {print $0}' poem.txt # 打印第 2 到第 4 行 awk 'NR>=2 && NR<=4 {print $0}' poem.txtawk 中可以直接在花括号前写条件,只有条件为真时才执行后面的动作。
任务2:按列处理与分隔符
awk 最强大的能力之一,就是“按列(字段)处理”。默认用空白符(空格或 TAB)作为字段分隔符,分别用 $1、$2、…、$NF 引用各字段。
-
打印简单文本中的字段:
$1、$2、$NF# 打印每行的第 1 个单词 awk '{print $1}' poem.txt # 打印每行的第 1 个和第 2 个单词 awk '{print $1, $2}' poem.txt # 打印每行的第 1 个和最后 1 个单词 awk '{print $1, $NF}' poem.txtNF表示当前行的字段总数,因此$NF就是“最后一个字段”。 -
使用
-F指定分隔符:处理配置文件config.txt中使用=分隔键和值,可以通过-F设置字段分隔符。# 打印每一行的“键”和“值” awk -F '=' '{print $1, $2}' config.txt # 在输出中加上简单标签 awk -F '=' '{print "KEY:", $1, "VALUE:", $2}' config.txt -
处理 passwd.sample:冒号分隔
/etc/passwd每行的格式一般为:用户名:密码占位:UID:GID:注释:家目录:Shell。# 打印用户名和 UID awk -F ':' '{print $1, $3}' passwd.sample # 打印用户名和家目录 awk -F ':' '{print "User:", $1, "Home:", $6}' passwd.sample
任务3:条件与模式匹配
awk 中的“模式 + 动作”非常常用:模式 { 动作 }。模式可以是行号条件、字段条件,也可以是“包含某个字符串”的模式匹配。
-
根据行号筛选:
NR# 只打印第 3 行 awk 'NR==3 {print $0}' poem.txt # 打印第 2~4 行,并附带行号 awk 'NR>=2 && NR<=4 {print NR, $0}' poem.txt -
模式匹配:
/字符串/直接使用
/模式/作为条件,表示“只匹配包含此模式的行”。# 打印包含 ERROR 的日志行 awk '/ERROR/ {print $0}' app.log # 打印包含 Linux 或 linux 的行(简单写两次) awk '/Linux/ {print $0}' poem.txt awk '/linux/ {print $0}' poem.txt说明:awk 支持正则表达式,但本实验只使用简单的“包含某字符串”的用法。
-
按字段条件筛选:字段比较
可以先设置合适的分隔符,然后用字段值进行比较,只对满足条件的行执行动作。
# 从 config.txt 中找出键为 PORT 的行 awk -F '=' '$1=="PORT" {print $0}' config.txt # 从 passwd.sample 中筛选 UID >= 1000 的行,并显示用户名与 UID awk -F ':' '$3>=1000 {print $1, $3}' passwd.sample
任务4:内置变量与BEGIN/END
awk 提供了很多内置变量,用于描述当前行、当前字段等信息。本任务选取最常用的一些:NR、NF、FS、OFS,并结合 BEGIN/END 进行简单格式控制。
-
查看每行字段数:
NF# 打印每行有多少个字段,以及原始内容 awk '{print "NF=" NF, ":", $0}' poem.txtNF是“Number of Fields”的缩写,表示当前行的字段个数。 -
在 BEGIN 中设置分隔符:
FS除了命令行参数
-F,也可以在BEGIN块中设置字段分隔符FS。# 用 FS 在 BEGIN 中设置分隔符 awk 'BEGIN {FS="="} {print $1, $2}' config.txt -
使用 OFS 控制输出分隔符
默认情况下,
print $1, $2会用空格分隔字段;通过OFS可以修改输出字段分隔符。# 在 BEGIN 中同时设置输入和输出分隔符 awk 'BEGIN {FS=":"; OFS="\t"} {print $1, $3}' passwd.sample上例中,输入按
:拆分字段,输出时使用制表符\t连接字段,更利于对齐。 -
BEGIN / END 块的基本用法
BEGIN在任何输入行处理前执行一次,END在所有输入处理完毕后执行一次。# 在处理前后打印提示信息 awk 'BEGIN {print "=== Start awk demo ==="} {print NR, $0} END {print "=== End awk demo ==="}' poem.txt
任务5:简单统计与小实战
本任务通过一些“计数、求和”的小例子,让你体会 awk 作为“小型脚本语言”的能力,仍然保持语法简单,不涉及函数、数组等高级特性。
一、对日志进行简单计数
统计 app.log 中包含不同级别关键字的行数,例如 ERROR、WARN。
# 统计包含 ERROR 的行数
awk '/ERROR/ {count++} END {print "ERROR 行数:", count}' app.log
# 同理,也可以统计 WARN 行数
awk '/WARN/ {count++} END {print "WARN 行数:", count}' app.log
二、对数字文件求和与平均值
使用 numbers.txt,每行一个整数,利用 awk 求和并计算平均值。
# 对 numbers.txt 中的数字求和与平均值
awk '{sum += $1} END {print "sum =", sum, "avg =", sum/NR}' numbers.txt
说明:这里使用了两个自定义变量 sum 和 NR:
- 对每一行,执行
sum += $1累加第一列的值。 - 在
END中,使用NR(总行数)计算平均值。
三、小实战:配置与用户信息分析(不使用管道)
-
统计 config.txt 中有效配置项数量
忽略以
#开头的注释行,只统计实际配置行的数量。# 统计非注释行(简单判断首字符不是 #) awk 'substr($1,1,1)!="#"{count++} END {print "有效配置项数量:", count}' config.txt -
统计 passwd.sample 中普通用户数量
约定:UID >= 1000 为普通用户(与 sed 实验保持一致)。
# 统计 UID>=1000 的用户数量 awk -F ':' '$3>=1000 {count++} END {print "普通用户数量:", count}' passwd.sample -
打印普通用户的“用户名 + 家目录”列表
# 打印 UID>=1000 的用户名与家目录 awk -F ':' '$3>=1000 {print "User:", $1, "Home:", $6}' passwd.sample
四、实验报告要求
- 逐条记录各个任务中关键
awk命令的使用方法与执行结果(可包含命令、输入文件片段、输出截图)。 - 总结
awk中“模式 + 动作”的基本结构,以及NR、NF、FS、OFS等内置变量的作用。 - 解释
awk '条件 { 动作 }'语法中“条件”和“动作”的含义,并列举至少 3 个具体例子(含行号条件、字段条件、模式匹配等)。 - 以
config.txt或passwd.sample为例,自行设计一个简单的“统计或筛选”需求,并通过awk给出实现方案和步骤说明。 - 对比
awk与sed、grep的侧重点,思考在实际运维/数据处理场景中各自适用的场景,例如:grep更偏向于“查找行”。sed更适合“对行做替换、插入、删除”。awk更适合“按列处理、做简单统计和报表”。
五、常见问题解答
-
Q:为什么我用
awk '{print $1}' file打印出来的内容怪怪的?
A:awk 默认按空白字符(空格或 TAB)作为字段分隔符,如果你的文件是用:或=分隔的,需要用-F或FS来指定分隔符,例如:awk -F ':' '{print $1}' passwd.sample awk -F '=' '{print $1}' config.txt -
Q:
NR和NF有什么区别?
A:NR表示当前处理的是“第几行”(Number of Records),NF表示当前行有多少个字段(Number of Fields)。例如:
会在每行前看到“行号”和“字段数”。awk '{print NR, NF, $0}' poem.txt -
Q:什么时候需要使用
BEGIN/END?
A:当你需要在“处理前”做一些初始化(如设置FS、打印标题),或在“处理结束后”统一打印统计结果(如总行数、总和、平均值)时,就可以使用BEGIN/END块。例如:awk 'BEGIN {print "Start"} {sum += $1} END {print "Sum =", sum}' numbers.txt -
Q:本实验为什么不使用管道?
A:本实验定位为 awk 入门,目的是先熟悉 awk 自己的语法和思想:模式 + 动作、按字段处理、BEGIN/END 等。管道组合(如ps aux | awk ...)在后续 Linux 进阶或综合实验中再展开会更合适。 -
Q:awk 和 sed、grep 比较,各自更适合做什么?
A:一般可粗略理解为:grep:用来“找行”,从大量文本中筛选出包含某模式的行。sed:用来“改行”,对选中的行进行替换、删除、插入等编辑操作。awk:用来“算和列”,对选中行的各个字段进行格式化输出、统计、生成简单报表。