Awk入门
数组(Arrays)
Awk 允许使用数组。如果你有编程经验,应该已经熟悉数组的概念。简单来说,数组是一个变量,可以存储多个值,类似于列表(list)。在 Awk 中,数组不需要声明,变量命名规则与普通变量相同。
1. Awk 数组的基本用法
Awk 的数组是单维(one-dimensional)的,索引从 1
开始。数组的元素使用方括号 []
访问:
some_array[1] = "Hello"
some_array[2] = "Everybody"
some_array[3] = "!"
print some_array[1], some_array[2], some_array[3]
输出:
Hello Everybody !
索引不需要连续,未使用的索引不会被自动填充。
2. 关联数组(Associative Arrays)
不同于 C 语言的数组,Awk 的数组是“关联数组”(Associative Arrays),索引可以是字符串,类似于 Python 的字典(dictionary)。
debts["Kim"] = 50
debts["Roberto"] += 70
debts["Vic"] -= 30
print "Vic paid 30 dollars, but still owes", debts["Vic"]
C 风格数组 vs 关联数组
C 风格数组 | Awk 关联数组 |
---|---|
固定长度 | 动态增长 |
整数索引(从 0 开始) | 字符串索引 |
多维数组 | 只有一维(但可以模拟多维) |
顺序存储 | 无固定顺序(无序) |
注意:Awk 只有关联数组,没有传统的 C 风格数组。
3. 数组的特性
3.1 动态增长
Awk 数组的大小是动态的,无需预定义长度。当访问未初始化的索引时,Awk 会自动创建该元素:
message[1] = "Have a nice"
message[2] = "day."
print message[1], message[2], message[3] # message[3] 未初始化
输出:
Have a nice day.
由于 message[3]
没有赋值,Awk 将其初始化为空字符串(""
),因此输出不会包含额外内容。
3.2 删除数组元素
使用 delete
关键字可以删除数组元素:
delete message[2] # 删除第二个元素
message[3] = "night."
print message[1], message[2], message[3]
输出:
Have a nice night.
message[2] 已被删除,所以 Awk 认为它是空字符串。
3.3 删除整个数组
某些 Awk 版本(如 gawk
和 mawk
)支持删除整个数组:
delete message # 删除整个数组
删除后,message
数组不再存在。
4. 字符串索引
Awk 数组的索引可以是字符串:
translate["table"] = "mesa"
translate["chair"] = "silla"
translate["good"] = "bueno"
但数值索引也完全可行:
translate[1] = "uno"
translate[5] = "cinco"
4.1 注意:使用小数索引
如果使用小数索引,Awk 可能无法正确匹配:
problems[ (1/3) ] = "one third"
你不能使用 problems[0.333]
访问这个元素,因为 Awk 会将数值转换为字符串,格式取决于 OFMT
变量:
BEGIN { print (1/3) }
默认情况下,输出可能是 0.333333
,因此 problems[0.333]
可能不会找到正确的键。
建议:
- 避免使用小数作为索引,或确保格式化一致。
5. 数组的无序性
Awk 关联数组是无序的:
debts["Alice"] = 100
debts["Bob"] = 200
debts["Charlie"] = 50
元素的存储顺序不固定,但可以使用 for in
遍历:
for (person in debts)
print person, "owes", debts[person]
可能的输出:
Charlie owes 50
Alice owes 100
Bob owes 200
(顺序可能不同)
5.1 检查元素是否存在
if ("Kane" in debts)
print "Kane owes", debts["Kane"]
in
关键字用于检查某个索引是否存在。
6. 模拟多维数组
Awk 不支持真正的多维数组,但可以使用 SUBSEP
模拟多维数组:
array["NY", "capital"] = "Albany"
array["NY", "big city"] = "New York City"
array["OR", "capital"] = "Salem"
array["OR", "big city"] = "Portland"
等价于:
array["NY@capital"] = "Albany"
array["NY@big city"] = "New York City"
array["OR@capital"] = "Salem"
array["OR@big city"] = "Portland"
默认情况下,SUBSEP = "@"
,但可以修改:
BEGIN { SUBSEP = ":" }
这样 array["NY", "capital"]
就等价于 array["NY:capital"]
。
7. gawk
提供的数组函数
标准 Awk 没有内置数组函数,但 gawk
提供了排序等功能:
函数 | 作用 |
---|---|
length(A) |
返回数组 A 的长度 |
asort(A[,B]) |
对 A 进行排序,索引变为 1,2,3... |
asorti(A[,B]) |
对 A 的索引排序,索引变为 1,2,3... ,值变为原来的索引 |
示例:排序数组
BEGIN {
arr["Bob"] = 30
arr["Alice"] = 20
arr["Charlie"] = 40
asorti(arr, sorted)
for (i in sorted)
print sorted[i], arr[sorted[i]]
}
8. 练习
练习 1:统计硬币数量
修改之前的 "coins" 程序,使用数组按国家统计硬币数量:
/gold|silver/ { count[$4]++ }
END {
for (country in count)
print country, "has", count[country], "coins"
}
练习 2:债务追踪
读取日志,记录借款和还款:
Jim owes 50
Kim paid 30
Jim owes 20
Kim owes 10
代码:
{
if ($2 == "owes") debts[$1] += $3
else if ($2 == "paid") debts[$1] -= $3
}
END {
for (person in debts)
print person, "owes", debts[person]
}
练习 3:删除已还清的债务
END {
for (person in debts)
if (debts[person] != 0)
print person, "owes", debts[person]
}
这样,欠款为 0 的人不会出现在结果中。
总结
- Awk 只有关联数组,索引可以是字符串或数字。
- 数组是动态的,大小可变。
- 支持删除单个元素或整个数组。
- 无序存储,使用
for in
遍历。 - 可用
SUBSEP
模拟多维数组。 gawk
提供排序函数。
在下一章,我们将学习 Awk 的运算符(Operators) 🚀