Linux基础及应用教程:文本三剑客之awk

一、实验目标

二、实验环境

三、实验内容与步骤

文件准备(创建实验环境)

本实验继续使用 自建测试文本系统自带配置文件的副本 进行练习,方便同时比较 sedawk 的不同用法。

# 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 的基本行为:逐行读取、按字段拆分,并对每行执行 { 动作 }

  1. 查看文件内容:打印整行($0

    # 使用 awk 打印整行(等价于 cat)
    awk '{print}' poem.txt
    
    # 明确写出 $0,表示“当前整行”
    awk '{print $0}' poem.txt

    说明:$0 表示当前整行内容,是 awk 默认处理的对象。

  2. 打印行号:NR

    # 在每行前面加上行号(NR)
    awk '{print NR, $0}' poem.txt

    NR 是 awk 的内置变量,表示当前处理的是“第几行”。

  3. 打印指定行(用 NR 做简单条件)

    # 打印第 1 行
    awk 'NR==1 {print $0}' poem.txt
    
    # 打印第 2 到第 4 行
    awk 'NR>=2 && NR<=4 {print $0}' poem.txt

    awk 中可以直接在花括号前写条件,只有条件为真时才执行后面的动作。

任务2:按列处理与分隔符

awk 最强大的能力之一,就是“按列(字段)处理”。默认用空白符(空格或 TAB)作为字段分隔符,分别用 $1$2、…、$NF 引用各字段。

  1. 打印简单文本中的字段:$1$2$NF

    # 打印每行的第 1 个单词
    awk '{print $1}' poem.txt
    
    # 打印每行的第 1 个和第 2 个单词
    awk '{print $1, $2}' poem.txt
    
    # 打印每行的第 1 个和最后 1 个单词
    awk '{print $1, $NF}' poem.txt

    NF 表示当前行的字段总数,因此 $NF 就是“最后一个字段”。

  2. 使用 -F 指定分隔符:处理配置文件

    config.txt 中使用 = 分隔键和值,可以通过 -F 设置字段分隔符。

    # 打印每一行的“键”和“值”
    awk -F '=' '{print $1, $2}' config.txt
    
    # 在输出中加上简单标签
    awk -F '=' '{print "KEY:", $1, "VALUE:", $2}' config.txt
  3. 处理 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 中的“模式 + 动作”非常常用:模式 { 动作 }。模式可以是行号条件、字段条件,也可以是“包含某个字符串”的模式匹配。

  1. 根据行号筛选:NR

    # 只打印第 3 行
    awk 'NR==3 {print $0}' poem.txt
    
    # 打印第 2~4 行,并附带行号
    awk 'NR>=2 && NR<=4 {print NR, $0}' poem.txt
  2. 模式匹配:/字符串/

    直接使用 /模式/ 作为条件,表示“只匹配包含此模式的行”。

    # 打印包含 ERROR 的日志行
    awk '/ERROR/ {print $0}' app.log
    
    # 打印包含 Linux 或 linux 的行(简单写两次)
    awk '/Linux/ {print $0}' poem.txt
    awk '/linux/ {print $0}' poem.txt

    说明:awk 支持正则表达式,但本实验只使用简单的“包含某字符串”的用法。

  3. 按字段条件筛选:字段比较

    可以先设置合适的分隔符,然后用字段值进行比较,只对满足条件的行执行动作。

    # 从 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 提供了很多内置变量,用于描述当前行、当前字段等信息。本任务选取最常用的一些:NRNFFSOFS,并结合 BEGIN/END 进行简单格式控制。

  1. 查看每行字段数:NF

    # 打印每行有多少个字段,以及原始内容
    awk '{print "NF=" NF, ":", $0}' poem.txt

    NF 是“Number of Fields”的缩写,表示当前行的字段个数。

  2. 在 BEGIN 中设置分隔符:FS

    除了命令行参数 -F,也可以在 BEGIN 块中设置字段分隔符 FS

    # 用 FS 在 BEGIN 中设置分隔符
    awk 'BEGIN {FS="="} {print $1, $2}' config.txt
  3. 使用 OFS 控制输出分隔符

    默认情况下,print $1, $2 会用空格分隔字段;通过 OFS 可以修改输出字段分隔符。

    # 在 BEGIN 中同时设置输入和输出分隔符
    awk 'BEGIN {FS=":"; OFS="\t"} {print $1, $3}' passwd.sample

    上例中,输入按 : 拆分字段,输出时使用制表符 \t 连接字段,更利于对齐。

  4. BEGIN / END 块的基本用法

    BEGIN 在任何输入行处理前执行一次,END 在所有输入处理完毕后执行一次。

    # 在处理前后打印提示信息
    awk 'BEGIN {print "=== Start awk demo ==="}
         {print NR, $0}
         END   {print "=== End awk demo ==="}' poem.txt

任务5:简单统计与小实战

本任务通过一些“计数、求和”的小例子,让你体会 awk 作为“小型脚本语言”的能力,仍然保持语法简单,不涉及函数、数组等高级特性。

一、对日志进行简单计数

统计 app.log 中包含不同级别关键字的行数,例如 ERRORWARN

# 统计包含 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

说明:这里使用了两个自定义变量 sumNR

三、小实战:配置与用户信息分析(不使用管道)

  1. 统计 config.txt 中有效配置项数量

    忽略以 # 开头的注释行,只统计实际配置行的数量。

    # 统计非注释行(简单判断首字符不是 #)
    awk 'substr($1,1,1)!="#"{count++} END {print "有效配置项数量:", count}' config.txt
  2. 统计 passwd.sample 中普通用户数量

    约定:UID >= 1000 为普通用户(与 sed 实验保持一致)。

    # 统计 UID>=1000 的用户数量
    awk -F ':' '$3>=1000 {count++} END {print "普通用户数量:", count}' passwd.sample
  3. 打印普通用户的“用户名 + 家目录”列表

    # 打印 UID>=1000 的用户名与家目录
    awk -F ':' '$3>=1000 {print "User:", $1, "Home:", $6}' passwd.sample

四、实验报告要求

五、常见问题解答

回到首页 回到主页