Awk 入门/数组
Awk 也允许使用数组。那些以前编写过程序的人已经熟悉数组。对于那些没有接触过数组的人来说,数组只是一个保存多个值的单个变量,类似于列表。命名约定与变量相同,并且,与变量一样,数组不需要声明。
Awk 数组只能有一维;第一个索引为 1。数组元素通过索引来识别,索引包含在方括号中。例如
some_array[1] = "Hello" some_array[2] = "Everybody" some_array[3] = "!" print some_array[1], some_array[2], some_array[3]
括号内的数字是索引。它选择数组内的特定条目,或元素,然后可以像普通变量一样创建、访问或修改它。
这是熟悉 C 风格数组的人学习新事物的地方。Awk 数组很有趣,因为它们实际上是关联数组。索引实际上是字符串,因此关联数组更像字典而不是编号列表。例如,数组可以用来统计一组债务人所欠的钱,如下所示
debts["Kim"] = 50 debts["Roberto"] += 70 debts["Vic"] -= 30 print "Vic paid 30 dollars, but still owes", debts["Vic"]
C 风格数组(表现得像编号列表)和关联数组(表现得像字典)之间存在一些差异
C 风格数组 | 关联数组 |
---|---|
|
|
Awk 只有关联数组,没有 C 风格数组。这种比较只针对那些从其他编程语言学习数组的人。
让我们回顾一下 Awk 数组的一些更具体的特征
Awk 中的数组可以在程序执行过程中增长和缩小。每当访问 Awk 之前从未见过的索引时,新条目会自动创建。无需让 Awk 知道您打算使用多少个元素。
message[1]="Have a nice" message[2]="day." print message[1], message[2], message[3]
在这个例子中,数组 message
中的元素 1 和 2 在我们为它们分配值的那一刻就被创建。在最后一行,访问 message[3]
,尽管它还没有创建。Awk 将创建元素 message[3]
并将其初始化为空字符串(因此什么都不会出现)。
此外,可以从数组中删除元素。以下是上述示例的扩展
delete message[2] message[3]="night." print message[1], message[2], message[3]
现在,message[2]
不再存在。就好像您从未在第一次为它赋值一样,因此 Awk 将对它的提及视为空字符串。如果您一起运行这两个示例,结果将是
Have a nice day. Have a nice night.
(注意 print
语句中的逗号如何在数组元素之间添加空格。这可以通过设置内置变量 OFS
来更改。)
一些实现,比如 gawk 或 mawk,也允许程序员删除整个数组而不是单个元素。在
delete message
之后,数组 message
不再存在。
您已经看到 Awk 数组使用字符串来选择数组中的每个元素,就像字典一样。
translate["table"] = "mesa" translate["chair"] = "silla" translate["good"] = "bueno"
但是,数字是完全可以接受的。像往常一样,Awk 只会在需要时将数字转换为字符串。
translate[1] = "uno" translate[5] = "cinco"
然而,当用小数访问数组时,事情可能会变得棘手。
problems[ (1/3) ] = "one third"
您可以使用 problems[0.333]
访问此元素吗?不。这取决于内置变量 OFMT
的内容,它告诉 Awk 如何将数字转换为字符串。将转换一定数量的小数位数,其余的将被丢弃。一般来说,尽量避免使用带小数的索引,除非您非常小心地使用正确的格式(可以更改)。
Awk 数组是稀疏的,这意味着您可以有元素 1 和元素 3 而没有元素 2。这很明显——Awk 使用字符串索引,因此它不区分编号元素。
更重要的是,关联数组中的元素不会按任何特定顺序存储。
有两个有用的命令允许您检查数组中的元素。我们将在接下来的章节中详细了解它们,但现在让我们看一些示例。
if( "Kane" in debts ) print "Kane owes", debts["Kane"]
for( person in debts ) print person, "owes", debts[person]
回到介绍(其中创建了一个 debts
数组将人们的姓名与金额相关联),我们可以看到 Awk 提供了一些有用的命令来访问数组。第一个命令,if in
,让我们检查特定元素是否已定义,然后根据该结果执行代码。第二个命令,for in
,让我们创建一个临时变量(在本例中称为 person
),并在数组中的每个元素上重复一条语句。
玩弄这些例子,看看 Awk 如何不一定会在其数组中保持特定的顺序。幸运的是,这从来都不是真正的问题。
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"
这只是多维数组的快速演示。如您所见,这些实际上不是多维的;而是单维的,但使用特殊的分隔符。此处不会进一步探讨多维数组,因为必须理解几个技术细节。您仍然可以在没有它们的情况下编写有用的 Awk 程序,但如果您对多维数组感兴趣,请随时查阅您的 Awk 手册(或者只是玩弄一下看看什么有效)。
标准 Awk 中没有用于数组的函数。但是,gawk 提供了三个函数(A 和 B 应该都是数组)
- length(A) 返回 A 的长度。
- asort(A[,B]) - 如果未给出 B,则对 A 进行排序。A 的索引将被替换为从 1 开始的连续整数。如果给出 B,则将 A 复制到 B,然后按上述方式对 B 进行排序,而 A 保持不变。返回 A 的长度。
- asorti(A[,B]) - 如果未给出 B,则丢弃 A 的值并对它的索引进行排序。排序后的索引将成为新的值,从 1 开始的连续整数将成为新的索引。与前一种情况类似,如果给出 B,则将 A 复制到 B,然后按上述方式对 B 的索引进行排序,而 A 保持不变。返回 A 的长度。
- 更新我们在本书开头编写的“coins”程序。使用数组来统计按国家分类的硬币数量。显示结果以及摘要。
- 编写债务人程序。它应该扫描一个日志文件,该文件列出了像“Jim owes 50”和“Kim paid 30”这样的交易。使用关联数组,对人们借入和偿还的所有钱进行汇总。确保一个人可以在文件中多次出现,并且他们的债务会得到相应的更新。在
END
处,列出每个人及其总额。 - 改进#2中的程序,如果一个人偿还了所有欠款,则从数组中删除他们的姓名。这样,结果就不会被欠零元的人弄得乱七八糟。
下一页快速回顾了 Awk 提供的所有运算符。