关注

【Linux】Shell编程(三):awk - 文本分析工具 | 第一篇

本专栏文章持续更新,新增内容使用蓝色表示。

AWK 是一种强大的文本分析工具,以其三位创始人 Alfred Aho、Peter Weinberger 和 Brian Kernighan 的姓氏首字母命名。自 1977 年诞生以来,它已成为 Unix/Linux 系统中不可或缺的数据处理工具,特别擅长查询统计分析结构化文本数据。

简单来说,AWK 的工作模式可以总结为 “读取-分割-处理” 就是把文件逐行读入,以空格作为默认分割符将每行切片,切开的部分再进行各种分析处理。

食用指南

在开始 awk 练习之前,建议先快速浏览前面部分的内容,对 awk 的基本概念和功能有个整体印象。不需要死记硬背所有选项和语法,重点是了解 awk 能做什么。

练习部分建议:阅读完题目之后自行尝试解决,遇到问题可以回到前面的"选项"、"模式"、"操作"等章节查找对应的解决方法。解决完问题之后,可自行拓展其他的复杂场景。

语法:

awk [options] 'pattern{action}' {filenames}

一、常用选项 (options)

选项说明
-F指定字段分隔符
-v定义变量
-f从文件读取awk脚本

内置变量

变量说明
$0当前整行内容
$1,$2,...$n第1,2,...n个字段
NF当前行的字段数量
NR当前处理的行号
FNR当前文件中的行号
FS设置输入字段分隔符(默认空格),等价于 -F 选项
OFS输出字段分隔符(默认空格)
RS输入记录分隔符(默认换行)
ORS输出记录分隔符(默认换行)
FILENAMEawk 浏览的文件名
$NFnumber finally,表示最后一列的信息。

二、模式 (pattern) 类型

模式类型语法
正则表达式/regex/
行号范围NR==n
字段比较$n operator value
特殊模式BEGIN、END、BEGINFILE、 ENDFILE
条件组合&& || !

特殊模式

BEGIN - 在处理任何输入行之前执行

# 初始化操作
awk 'BEGIN {
    FS=":"; OFS="\t"; 
    print "开始处理文件..."
} 
{print $1, $3}' /etc/passwd

# 设置表头
awk 'BEGIN {print "姓名\t薪资"} {print $1, $4}' employees.txt

END - 在处理完所有输入行之后执行

# 统计总结
awk '{sum += $4} END {print "总薪资:", sum}' employees.txt

# 多文件统计
awk '{count++} END {print "总行数:", count}' *.txt

BEGINFILE / ENDFILE - 处理每个文件开始和结束

# 为每个文件生成统计
awk '
BEGINFILE {print "处理文件:", FILENAME; count=0}
{count++}
ENDFILE {print "本文件行数:", count; total+=count}
END {print "所有文件总行数:", total}' file1.txt file2.txt

三、常用操作 (action)

操作语法
打印print
格式化打印printf
变量赋值var=value
流程控制if-else,for, while
数组操作array[index]

流程控制

if-else - 条件判断

# 简单条件
awk '{if($4 > 6000) print $1, "高薪"; else print $1, "普通"}' employees.txt

# 嵌套条件
awk '{
    if($4 > 8000) grade="A"
    else if($4 > 5000) grade="B" 
    else grade="C"
    print $1, grade
}' employees.txt

for循环 - 固定次数循环

# 遍历字段
awk '{for(i=1;i<=NF;i++) print "字段" i ":", $i}' employees.txt

# 数字范围循环
awk 'BEGIN{for(i=1;i<=5;i++) print "数字:", i}'

# 倒序循环
awk '{for(i=NF;i>=1;i--) print $i}'

while循环 - 条件循环

# 条件循环
awk '{i=1; while(i<=NF) {print $i; i++}}' employees.txt

# 模拟do-while
awk '{i=1; do {print $i; i++} while(i<=3)}'  # 至少执行一次

for...in循环 - 遍历数组

# 遍历关联数组
awk '{ips[$1]++} END{for(ip in ips) print ip, ips[ip]}' access.log

# 按特定顺序遍历
awk '{arr[$1]++} END{for(i in arr) print i, arr[i] | "sort -k2nr"}'

break / continue - 循环控制

# break示例
awk '{
    for(i=1;i<=NF;i++) {
        if($i == "stop") break
        print $i
    }
}' data.txt

# continue示例  
awk '{
    for(i=1;i<=NF;i++) {
        if($i == "skip") continue
        print $i
    }
}' data.txt

next - 跳过当前记录

# 跳过空行
awk '/^$/ {next} {print "非空行:", $0}' file.txt

# 跳过特定模式
awk '/^#/ {next} {print $0}' config.txt  # 跳过注释行

exit - 立即退出

# 找到特定记录后退出
awk '/error/ {print "发现错误:", $0; exit}' logfile.txt

# 限制处理行数
awk 'NR > 100 {print "超过100行,退出"; exit} {print $0}' largefile.txt

补充:sort

sort 命令是 Linux/Unix 系统中的独立命令行工具。语法格式如下:

sort [选项] [文件]

排序类型选项

选项说明
-n数字排序
-r降序排序
-f忽略大小写
-R随机排序

字段控制选项

选项说明备注
-k指定排序字段

-k1 = 从第1字段到行尾

-k3,3 = 仅第3字段

sort命令按 -k 选项的顺序决定优先级

-t指定字段分隔符
-u去重排序

四、awk 练习

4.1 数据准备

# 创建练习目录和文件
mkdir awk-practice && cd awk-practice

4.2 基础输出

先进行一个简单的尝试:

echo 'apple banana tomato potato' | awk '{print$0}'

echo 'apple banana tomato potato' | awk '{print$3}'

echo 'apple banana tomato potato' | awk '{print$1"+"$3"+"$4}'

4.3 日志分析练习 (access.log)

# 创建日志文件
cat > access.log << 'EOF'
192.168.1.100 - - [10/Oct/2023:10:30:01] "GET /index.html HTTP/1.1" 200 1524
192.168.1.101 - - [10/Oct/2023:10:30:02] "POST /login HTTP/1.1" 302 0
192.168.1.102 - - [10/Oct/2023:10:30:03] "GET /products HTTP/1.1" 404 234
192.168.1.100 - - [10/Oct/2023:10:30:04] "GET /about.html HTTP/1.1" 200 2100
192.168.1.103 - - [10/Oct/2023:10:30:05] "GET /index.html HTTP/1.1" 200 1524
192.168.1.101 - - [10/Oct/2023:10:30:06] "GET /contact HTTP/1.1" 200 1800
192.168.1.104 - - [10/Oct/2023:10:30:07] "POST /register HTTP/1.1" 500 123
192.168.1.102 - - [10/Oct/2023:10:30:08] "GET /images/logo.png HTTP/1.1" 200 5432
EOF

文件内容示例如下:

4.3.1 提取所有IP地址

awk '{print $1}' access.log

4.3.2 统计状态码出现次数

awk '{status[$8]++} END {for(code in status) print code,status[code]}' access.log

统计操作要在主块中完成,输出结果在END块中进行。

在这个基础上加上以出现次数为依据降序排序,如果次数相同,就以状态码为依据,进行降序排序。只需要在上一个命令的基础上加 sort 排序即可。

awk '{status[$8]++} END {for(code in status) print code, status[code]}' access.log | sort -k2,2nr -k1,1nr

4.3.3 统计所有的 GET 请求

awk '/GET/{print}' access.log

在这个结果基础之上筛选出状态码为 200 的 IP。

通过观察可以发现 IP 在第一列,状态码在第八列。

方法一:先过滤 GET 请求,再判断状态码

awk '/GET/ && $8==200 {print $1}' access.log

方法二:使用 if 语句

awk '/GET/ {if($8==200) print $1}' access.log

4.3.4 找出访问量最大的IP

awk '{nums[$1]++} END {for(ip in nums) print ip,nums[ip]}' access.log

练习其余部分见下一篇。


如有问题或建议,欢迎在评论区中留言~

转载自CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/D2005_10_25/article/details/154287817

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--