awk 命令详解:文本处理与数据提取的强大工具 awk 是 Linux 系统中处理文本数据的强大工具,尤其擅长按字段分割、提取关键信息、统计分析等任务。它将输入视为 “记录(行)” 和 “字段(列)” 的集合,通过 “模式 - 动作” 对实现灵活的文本处理。本文将系统讲解 awk 的核心用法、内建变量、高级特性及实战案例。
awk 基本工作原理与语法 核心概念
记录(Record) :默认以换行符(\n)分隔,即一行为一条记录。
字段(Field) :默认以空格或制表符分隔,每条记录可拆分为多个字段($1 表示第 1 个字段,$2 第 2 个,$0 表示整行)。
模式 - 动作对 :awk 程序由 pattern { action } 组成,当记录匹配 pattern 时,执行 action(动作)。
基本语法 1 awk [选项] 'pattern1 { action1 } pattern2 { action2 } ...' 输入文件
常用选项
-F fs:指定字段分隔符(fs 可以是字符、正则表达式),默认是空格 / 制表符。
-v var=value:在 awk 程序中定义变量(传递外部值到 awk)。
-f file:从文件 file 中读取 awk 程序(适合复杂脚本)。
基础示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 awk '{print $0}' data.txt awk '{print $1, $3}' data.txt awk 'NR==2 {print $0}' data.txt awk -F: '{print $1, $7}' /etc/passwd
核心内建变量 awk 提供了大量内建变量,用于获取记录 / 字段属性、控制分隔符等,常用变量如下:
变量
含义
$0
当前记录(整行内容)
$n
第 n 个字段(n 为正整数,$1 是第 1 列,以此类推)
NF
当前记录的字段总数($NF 表示最后一个字段)
NR
已处理的总记录数(行号,多文件时不重置)
FNR
当前文件的记录数(行号,多文件时切换文件会重置)
FS
输入字段分隔符(默认空格,等价于 -F 选项)
OFS
输出字段分隔符(默认空格,可自定义)
RS
输入记录分隔符(默认换行符 \n)
ORS
输出记录分隔符(默认换行符 \n)
FILENAME
当前处理的文件名
ARGC
命令行参数总数(包含 awk 本身)
ARGV
命令行参数数组(ARGV[0] 是 awk,ARGV[1] 开始是输入文件 / 参数)
变量使用示例 1 2 3 4 5 6 7 8 9 10 11 awk '{print "字段数:"NF", 最后一个字段:"$NF}' data.txt awk '{print FILENAME": FNR="FNR", NR="NR}' data1.txt data2.txt awk -F: -v OFS=" | " '{print $1, $3, $6}' /etc/passwd awk -v RS="" '{print "段落"NR":"$0"\n----"}' article.txt
BEGIN 与 END 块 awk 允许在处理数据前 / 后执行特定操作,通过 BEGIN 和 END 块实现:
BEGIN :在读取任何输入记录前执行(仅一次),适合初始化变量、打印表头。
END :在读取所有输入记录后执行(仅一次),适合汇总统计、打印结果。
示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 awk ' BEGIN { print "姓名 | 年龄" # 表头 print "------------" } NR>1 {print $1" | "$2} # 跳过标题行,打印数据 END { print "------------" print "共"NR-1"条记录" # 统计数据行数(减去标题行) } ' data.txtawk -F: ' BEGIN {sum=0} # 初始化总和 {sum += $3} # 累加 UID END {print "所有用户 UID 总和:"sum} # 输出结果 ' /etc/passwd
模式(Pattern):筛选记录的条件 awk 的 “模式” 用于筛选需要处理的记录,常见模式类型:
正则表达式模式 用 /正则表达式/ 匹配记录,仅处理包含匹配内容的行。
1 2 3 4 5 6 awk '/error/ {print $0}' app.log awk -F: '$1 ~ /^root/ {print $0}' /etc/passwd
条件判断模式 用比较运算符(==、!=、>、<、>=、<=)或逻辑运算符(&&、||、!)筛选记录。
1 2 3 4 5 awk 'NR>1 && $2>28 {print $1" 年龄:"$2}' data.txt awk 'NR>1 && $3=="female" && $2<30 {print $1}' data.txt
行号模式 用 NR 或 FNR 指定行号范围。
1 2 3 4 5 awk 'NR>=2 && NR<=4 {print $0}' data.txt awk 'NF>0 {last=$0} END{print last}' data.txt
动作(Action):处理记录的操作 “动作” 是 awk 处理记录的核心,支持变量定义、运算、流程控制等,常用操作:
变量与运算 awk 支持整数 / 浮点数运算,变量无需声明,直接赋值即可。
1 2 3 4 5 6 7 8 9 10 11 awk ' NR>1 { sum += $2 # 累加年龄 count++ # 计数 } END { avg = sum / count printf "平均年龄:%.1f\n", avg # 格式化输出(保留1位小数) } ' data.txt
流程控制 支持 if-else、for、while、break、continue 等,实现复杂逻辑。
(1)if-else 条件判断 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 awk ' NR>1 { name = $1 age = $2 if (age < 30) { group = "青年" } else if (age <=50) { group = "中年" } else { group = "老年" } print name":"group } ' data.txt
(2)for 循环 1 2 3 4 5 6 7 8 9 10 11 awk ' { print "第"NR"行的非空字段:" for (i=1; i<=NF; i++) { # i 从1到字段数 NF if ($i != "") { # 跳过空字段 print " 字段"i":"$i } } } ' data.txt
(3)数组遍历 awk 支持关联数组(键可以是字符串),常用于计数、去重。
1 2 3 4 5 6 7 8 9 10 11 12 awk ' { ip = $1 count[ip]++ # 数组 count 记录每个 IP 的次数 } END { for (i in count) { # 遍历数组 print i" 访问次数:"count[i] } } ' /var/log/nginx/access.log
实战案例 1. 统计磁盘可用空间总和 1 2 3 4 5 6 7 8 df -P | grep -v tmpfs | awk ' NR>1 {sum += $4} # 跳过标题行,累加可用空间 END { # 转换为 GB(1GB = 1024*1024 KB) printf "总可用空间:%.2f GB\n", sum / 1024 / 1024 } '
2. 分析 Nginx 日志中访问量最高的前 10 个 IP 1 2 3 4 5 6 7 8 9 10 awk ' {ip[$1]++} # 统计每个 IP 的访问次数 END { # 将 IP 和次数存入数组,按次数倒序排序 for (i in ip) { print ip[i], i | "sort -k1,1nr | head -n10" } } ' /var/log/nginx/access.log
3. 提取 CSV 文件中特定列并去重 1 2 awk -F',' 'NR>1 {print $3}' user.csv | sort | uniq > emails.txt