一、awk基本概念
1.1 什么是awk
awk是一种强大的文本分析工具,用于对文本文件进行逐行处理。它支持模式匹配、数据提取、计算和格式化输出等功能。
1.2 基本语法
支持下面3种写法
bash
awk 'pattern {action}' input_file
awk -f script.awk input_file
script.awk input_file # 需要在script.awk第一行写"#!/bin/awk -f",并赋予文件可执行权限chmod +xscript.awk
二、awk工作原理
2.1 执行流程
-
读取输入文件的一行
-
按字段分隔符分割成字段
-
检查是否匹配pattern
-
如果匹配,执行action
-
重复直到文件结束
2.2 内置变量
| 变量 | 说明 | 示例 |
|---|---|---|
$0 |
整行内容 | {print $0} |
$1, $2... |
第1,2...个字段 | {print $1} |
NF |
当前行的字段数 | {print NF} |
NR |
当前行号 | {print NR} |
FNR |
当前文件的行号 | {print FNR} |
FS |
输入字段分隔符 | -F: 或 BEGIN{FS=":"} |
OFS |
输出字段分隔符 | BEGIN{OFS="\t"} |
RS |
输入记录分隔符 | BEGIN{RS="\n"} |
ORS |
输出记录分隔符 | BEGIN{ORS="\n\n"} |
FILENAME |
当前文件名 | {print FILENAME} |
三、常用用法示例
3.1 基本打印
bash
# 打印整行
awk '{print}' file.txt
awk '{print $0}' file.txt
# 打印第一列
awk '{print $1}' file.txt
# 打印多列
awk '{print $1, $3, $5}' file.txt
# 打印最后一列
awk '{print $NF}' file.txt
# 打印倒数第二列
awk '{print $(NF-1)}' file.txt
3.2 指定分隔符
bash
# 指定输入分隔符
awk -F: '{print $1}' /etc/passwd
awk 'BEGIN{FS=":"} {print $1}' /etc/passwd
# 同时指定输入输出分隔符
awk -F: 'BEGIN{OFS="\t"} {print $1, $3}' /etc/passwd
# 多个分隔符
awk -F'[:,]' '{print $1, $2}' file.txt
3.3 条件筛选
bash
# 匹配包含"error"的行
awk '/error/' log.txt
# 第三列等于"GET"
awk '$3 == "GET"' access.log
# 数值比较
awk '$4 > 100' data.txt
awk '$4 >= 100 && $4 <= 200' data.txt
# 第一列匹配正则
awk '$1 ~ /^192\.168/' log.txt
# 取反匹配
awk '!/error/' log.txt
awk '$1 !~ /^192\.168/' log.txt
3.4 BEGIN和END模式
bash
# 在开始前执行
awk 'BEGIN{print "开始处理"} {print $0}' file.txt
# 在结束后执行
awk '{sum+=$1} END{print "总和:", sum}' numbers.txt
# 结合使用
awk 'BEGIN{FS=":"; OFS="\t"; print "用户名\tUID"} {print $1, $3} END{print "处理完成"}' /etc/passwd
四、高级用法与技巧
4.1 计算与统计
bash
# 求和
awk '{sum+=$1} END{print sum}' numbers.txt
# 平均值
awk '{sum+=$1; count++} END{print "平均:", sum/count}' numbers.txt
# 最大值
awk 'NR==1{max=$1} $1>max{max=$1} END{print "最大值:", max}' numbers.txt
# 最小值
awk 'NR==1{min=$1} $1<min{min=$1} END{print "最小值:", min}' numbers.txt
# 统计行数
awk 'END{print NR}' file.txt
4.2 数组操作
bash
# 统计词频
awk '{for(i=1;i<=NF;i++) count[$i]++} END{for(word in count) print word, count[word]}' text.txt
# 按第一列分组求和
awk '{sum[$1]+=$2} END{for(key in sum) print key, sum[key]}' data.txt
# 去重
awk '!seen[$0]++' file.txt # 去重整行
awk '!seen[$1]++' file.txt # 按第一列去重
4.3 字符串处理
bash
# 字符串连接
awk '{print $1 "-" $2}' file.txt
# 字符串长度
awk '{print length($1)}' file.txt
# 子字符串
awk '{print substr($1, 2, 4)}' file.txt # 从第2个字符开始取4个
# 字符串替换
awk '{gsub(/old/, "new"); print}' file.txt
awk '{gsub(/old/, "new", $1); print $1}' file.txt
# 匹配位置
awk '{print index($0, "target")}' file.txt
4.4 流程控制
bash
# if语句
awk '{if($1>100) print $0}' data.txt
awk '{if($1>100) print "大"; else if($1>50) print "中"; else print "小"}' data.txt
# 三元运算符
awk '{print ($1>100 ? "高" : "低")}' data.txt
# for循环
awk '{for(i=1;i<=NF;i++) print i, $i}' file.txt
awk 'BEGIN{for(i=1;i<=5;i++) print i}'
# while循环
awk '{i=1; while(i<=NF){print $i; i++}}' file.txt
五、实用技巧集合
5.1 日志分析
bash
# 统计HTTP状态码
awk '{status[$9]++} END{for(s in status) print s, status[s]}' access.log
# 统计IP访问量
awk '{ip[$1]++} END{for(i in ip) print i, ip[i]}' access.log | sort -k2 -nr
# 提取时间段的日志
awk '/12\/Dec\/2023:10:/,/12\/Dec\/2023:11:/' access.log
# 统计接口响应时间大于1秒的请求
awk '$NF > 1 {print $7, $NF}' access.log | sort -k2 -nr
5.2 数据格式化
bash
# 对齐输出
awk '{printf "%-20s %10d\n", $1, $2}' data.txt
# 生成表格
awk 'BEGIN{printf "+----------------+----------+\n| 名称 | 数值 |\n+----------------+----------+"} {printf "| %-14s | %8d |\n", $1, $2} END{printf "+----------------+----------+\n"}' data.txt
# CSV转TSV
awk 'BEGIN{FS=","; OFS="\t"} {$1=$1; print}' data.csv
5.3 系统管理
bash
# 监控进程内存
ps aux | awk '$6>100000 {print $0}'
# 磁盘使用率
df -h | awk 'NR>1 && $5+0 > 80 {print "警告:", $1, "使用率:", $5}'
# 统计文件大小
ls -l | awk 'NR>1 {sum+=$5; count++} END{print "总数:", count, "总大小:", sum/1024/1024 "MB"}'
5.4 文本处理
bash
# 提取两个标记之间的内容
awk '/START/,/END/' file.txt
# 删除空行
awk 'NF' file.txt # 或 awk '!/^$/'
# 添加行号
awk '{print NR, $0}' file.txt
# 反转行序
awk '{lines[NR]=$0} END{for(i=NR;i>0;i--) print lines[i]}' file.txt
# 合并连续空行为一个
awk 'NF{print; blank=0} !NF{blank++; if(blank==1) print}'
六、实战示例
6.1 分析Nginx访问日志
bash
#!/bin/bash
# 分析日志脚本
LOG_FILE="access.log"
echo "=== 访问日志分析报告 ==="
echo "1. 总访问次数:"
awk 'END{print NR}' $LOG_FILE
echo -e "\n2. 独立IP数量:"
awk '{ip[$1]++} END{print length(ip)}' $LOG_FILE
echo -e "\n3. 最活跃的10个IP:"
awk '{ip[$1]++} END{for(i in ip) print ip[i], i}' $LOG_FILE | sort -nr | head -10
echo -e "\n4. HTTP状态码统计:"
awk '{status[$9]++} END{for(s in status) printf "%-4s: %d\n", s, status[s]}' $LOG_FILE
echo -e "\n5. 最受欢迎的10个页面:"
awk '{page[$7]++} END{for(p in page) print page[p], p}' $LOG_FILE | sort -nr | head -10
6.2 数据报表生成
bash
#!/usr/bin/awk -f
# sales_report.awk - 销售报表生成
BEGIN {
FS=","
OFS="\t"
print "销售报表"
print "=========================================="
print "销售人员\t销售额\t提成\t总工资"
print "------------------------------------------"
total_sales = 0
total_commission = 0
total_salary = 0
}
{
sales = $2
commission = sales * 0.1 # 10%提成
salary = 3000 + commission
total_sales += sales
total_commission += commission
total_salary += salary
printf "%s\t%.2f\t%.2f\t%.2f\n", $1, sales, commission, salary
}
END {
print "------------------------------------------"
printf "总计\t%.2f\t%.2f\t%.2f\n", total_sales, total_commission, total_salary
print "=========================================="
}
七、性能优化技巧
预处理模式匹配
bash
# 差的写法
awk '{if($1=="A" || $1=="B" || $1=="C") print}'
# 好的写法
awk '$1=="A" || $1=="B" || $1=="C"'
awk '$1 ~ /^(A|B|C)$/'
减少字段引用
bash
# 差的写法
awk '{print $1, $3, $5, $7, $9}'
# 好的写法
awk '{printf "%s %s %s %s %s\n", $1, $3, $5, $7, $9}'
使用内置函数代替shell命令
bash
# 避免调用外部命令
awk '{system("echo " $1)}' # 慢
awk '{print $1}' # 快
八、常见问题解答
Q1: awk与sed的区别?
-
awk:更适合处理结构化数据,支持字段操作、计算、数组
-
sed:更适合简单的文本替换、删除、插入
Q2: 如何处理大文件?
-
使用合适的字段分隔符
-
避免在action中调用外部命令
-
使用next跳过不相关的行
Q3: awk可以处理二进制文件吗?
- 不推荐,awk是文本处理工具,对于二进制文件应使用专门的工具
九、学习资源
-
官方文档 :
man awk,info awk -
经典书籍: 《sed与awk》
-
在线练习: 使用小文件进行测试
-
实践项目: 分析系统日志、处理CSV数据、生成报表
通过掌握这些用法和技巧,你可以高效地使用awk处理各种文本分析任务,大大提高工作效率。