一、awk基本概念
1.1 什么是awk
awk是一种强大的文本分析工具,用于对文本文件进行逐行处理。它支持模式匹配、数据提取、计算和格式化输出等功能。
1.2 基本语法
支持下面3种写法
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 基本打印
# 打印整行 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.txt3.2 指定分隔符
# 指定输入分隔符 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.txt3.3 条件筛选
# 匹配包含"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.txt3.4 BEGIN和END模式
# 在开始前执行 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 计算与统计
# 求和 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.txt4.2 数组操作
# 统计词频 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 字符串处理
# 字符串连接 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.txt4.4 流程控制
# 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 日志分析
# 统计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 -nr5.2 数据格式化
# 对齐输出 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.csv5.3 系统管理
# 监控进程内存 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 文本处理
# 提取两个标记之间的内容 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访问日志
#!/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 -106.2 数据报表生成
#!/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 "==========================================" }七、性能优化技巧
预处理模式匹配
# 差的写法 awk '{if($1=="A" || $1=="B" || $1=="C") print}' # 好的写法 awk '$1=="A" || $1=="B" || $1=="C"' awk '$1 ~ /^(A|B|C)$/'减少字段引用
# 差的写法 awk '{print $1, $3, $5, $7, $9}' # 好的写法 awk '{printf "%s %s %s %s %s\n", $1, $3, $5, $7, $9}'使用内置函数代替shell命令
# 避免调用外部命令 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处理各种文本分析任务,大大提高工作效率。