0%

awk命令

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
# 示例文件:data.txt
# name age gender
# Alice 25 female
# Bob 30 male
# Charlie 28 male


# 打印所有行($0 表示整行)
awk '{print $0}' data.txt

# 打印第1个和第3个字段(姓名和性别)
awk '{print $1, $3}' data.txt

# 仅打印第2行(NR 是内建变量,表示行号)
awk 'NR==2 {print $0}' data.txt

# 按冒号分隔字段(如 /etc/passwd),打印用户名(第1列)和 shell(第7列)
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]awkARGV[1] 开始是输入文件 / 参数)

变量使用示例

1
2
3
4
5
6
7
8
9
10
11
# 1. 打印每行的字段数(NF)和最后一个字段($NF)
awk '{print "字段数:"NF", 最后一个字段:"$NF}' data.txt

# 2. 处理多文件时,区分 FNR(当前文件行号)和 NR(总地行号)
awk '{print FILENAME": FNR="FNR", NR="NR}' data1.txt data2.txt

# 3. 自定义输出分隔符(OFS)
awk -F: -v OFS=" | " '{print $1, $3, $6}' /etc/passwd # 用 " | " 分隔输出字段

# 4. 以空行为记录分隔符(RS),处理段落文本
awk -v RS="" '{print "段落"NR":"$0"\n----"}' article.txt

BEGIN 与 END 块

awk 允许在处理数据前 / 后执行特定操作,通过 BEGINEND 块实现:

  • BEGIN:在读取任何输入记录前执行(仅一次),适合初始化变量、打印表头。
  • END:在读取所有输入记录后执行(仅一次),适合汇总统计、打印结果。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 1. 打印表头(BEGIN)+ 数据 + 汇总(END)
awk '
BEGIN {
print "姓名 | 年龄" # 表头
print "------------"
}
NR>1 {print $1" | "$2} # 跳过标题行,打印数据
END {
print "------------"
print "共"NR-1"条记录" # 统计数据行数(减去标题行)
}
' data.txt


# 2. 计算所有用户的 UID 总和(/etc/passwd 中第3列是 UID)
awk -F: '
BEGIN {sum=0} # 初始化总和
{sum += $3} # 累加 UID
END {print "所有用户 UID 总和:"sum} # 输出结果
' /etc/passwd

模式(Pattern):筛选记录的条件

awk 的 “模式” 用于筛选需要处理的记录,常见模式类型:

正则表达式模式

/正则表达式/ 匹配记录,仅处理包含匹配内容的行。

1
2
3
4
5
6
# 打印包含 "error" 的行(日志分析常用)
awk '/error/ {print $0}' app.log

# 打印第1个字段以 "root" 开头的行(/etc/passwd)
awk -F: '$1 ~ /^root/ {print $0}' /etc/passwd
# 注:~ 表示“匹配正则”,!~ 表示“不匹配”

条件判断模式

用比较运算符(==!=><>=<=)或逻辑运算符(&&||!)筛选记录。

1
2
3
4
5
# 打印年龄大于28的用户(data.txt 中第2列是年龄)
awk 'NR>1 && $2>28 {print $1" 年龄:"$2}' data.txt

# 打印性别为 female 且年龄小于30的用户
awk 'NR>1 && $3=="female" && $2<30 {print $1}' data.txt

行号模式

NRFNR 指定行号范围。

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

# 打印最后一行(NF>0 排除空行)
awk 'NF>0 {last=$0} END{print last}' data.txt

动作(Action):处理记录的操作

“动作” 是 awk 处理记录的核心,支持变量定义、运算、流程控制等,常用操作:

变量与运算

awk 支持整数 / 浮点数运算,变量无需声明,直接赋值即可。

1
2
3
4
5
6
7
8
9
10
11
# 计算平均年龄(data.txt)
awk '
NR>1 {
sum += $2 # 累加年龄
count++ # 计数
}
END {
avg = sum / count
printf "平均年龄:%.1f\n", avg # 格式化输出(保留1位小数)
}
' data.txt

流程控制

支持 if-elseforwhilebreakcontinue 等,实现复杂逻辑。

(1)if-else 条件判断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 分类用户年龄(青年:<30,中年:30-50,老年:>50)
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
# 统计 nginx 访问日志中各 IP 的访问次数(日志第1列是 IP)
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 查看磁盘信息,排除 tmpfs,第4列是可用空间(KB)
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
# 假设日志格式:IP 时间 "请求" 状态码 大小 ...
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
# 提取 user.csv 中第3列(邮箱),去重后保存到 emails.txt
awk -F',' 'NR>1 {print $3}' user.csv | sort | uniq > emails.txt

欢迎关注我的其它发布渠道