Awk入门
简介
在命令行中使用 Awk 处理文本文件非常简单。例如,假设我有一个名为 "coins.txt" 的文件,该文件描述了一组硬币的相关信息。文件中的每一行包含以下内容:
- metal(金属种类)
- weight in ounces(重量,单位:盎司)
- date minted(铸造年份)
- country of origin(原产国)
- description(描述)
该文件的内容如下:
gold 1 1986 USA American Eagle
gold 1 1908 Austria-Hungary Franz Josef 100 Korona
silver 10 1981 USA ingot
gold 1 1984 Switzerland ingot
gold 1 1979 RSA Krugerrand
gold 0.5 1981 RSA Krugerrand
gold 0.1 1986 PRC Panda
silver 1 1986 USA Liberty dollar
gold 0.25 1986 USA Liberty 5-dollar piece
silver 0.5 1986 USA Liberty 50-cent piece
silver 1 1987 USA Constitution dollar
gold 0.25 1987 USA Constitution 5-dollar piece
gold 1 1988 Canada Maple Leaf
筛选出所有的金质硬币
我可以使用 Awk 来筛选所有金质硬币的信息,命令如下:
awk '/gold/' coins.txt
该命令告诉 Awk 在文件中搜索包含 "gold"
的行,并将其打印出来。执行后,得到以下结果:
gold 1 1986 USA American Eagle
gold 1 1908 Austria-Hungary Franz Josef 100 Korona
gold 1 1984 Switzerland ingot
gold 1 1979 RSA Krugerrand
gold 0.5 1981 RSA Krugerrand
gold 0.1 1986 PRC Panda
gold 0.25 1986 USA Liberty 5-dollar piece
gold 0.25 1987 USA Constitution 5-dollar piece
gold 1 1988 Canada Maple Leaf
打印硬币描述
有批评者可能会说:“这很简单,任何 grep
或 find
工具都能做到。” 这的确没错,但 Awk 的功能远不止于此。例如,如果我只想打印硬币的描述字段,而不显示其他信息,我可以使用如下命令:
awk '/gold/ {print $5,$6,$7,$8}' coins.txt
运行后得到:
American Eagle
Franz Josef 100 Korona
ingot
Krugerrand
Krugerrand
Panda
Liberty 5-dollar piece
Constitution 5-dollar piece
Maple Leaf
此时,我们仅提取了硬币的 描述信息,而忽略了其他数据。
最简单的 Awk 程序
这个示例展示了 Awk 程序的最基本格式:
awk search_pattern { program_actions }
Awk 逐行扫描输入文件,寻找符合 search pattern(搜索模式) 的行,并对这些行执行指定的 program actions(程序操作)。在上面的例子中,具体的操作是:
{print $5,$6,$7,$8}
这里,print
语句的作用显而易见:它用于输出特定字段的值。而 $5, $6, $7, $8
是字段变量(field variables),它们按照行中的单词顺序存储文本内容。例如:
$1
代表当前行的第 1 个单词$2
代表第 2 个单词$3
代表第 3 个单词- 依此类推……
在 Awk 默认设置下,单词(字段)是由 空格 或 制表符 分隔的。
根据 "coins.txt" 文件的结构(见上文),可以将字段变量与文件的每一行内容匹配如下:
字段 | Awk 变量 |
---|---|
金属种类 | $1 |
重量(盎司) | $2 |
铸造年份 | $3 |
原产国 | $4 |
描述信息 | $5 ~ $8 |
通过 Awk,可以灵活地提取特定字段,并对其进行格式化、筛选、统计等各种处理,使其比 grep
等简单的文本搜索工具更加强大和实用。
程序操作
在上述示例中,程序的操作部分({print
$5,$6,$7,$8}
)用于打印包含硬币描述的字段。由于文件中的描述字段可能包含 1 到 4 个字段,但这并不会影响结果,因为 print
语句会自动忽略未定义的字段。
细心的读者可能已经注意到,coins.txt
文件的格式非常整齐,唯一包含多个字段的信息始终位于行尾。如果需要突破这个限制,可以修改字段分隔符,后续将会介绍如何更改。
Awk 默认的行为
Awk 默认的程序操作是打印整行,这意味着以下三个命令是等效的:
awk '/gold/' coins.txt
awk '/gold/ {print}'
awk '/gold/ {print $0}'
值得注意的是,Awk 识别 $0
变量,它表示整行文本。尽管 $0
在这里是冗余的,但它能够让程序的操作更加清晰。
条件判断
假设我想列出所有 1980 年之前铸造 的硬币,可以使用以下 Awk 命令:
awk '{if ($3 < 1980) print $3, " ",$5,$6,$7,$8}' coins.txt
执行后输出:
1908 Franz Josef 100 Korona
1979 Krugerrand
在这个新示例中,引入了一些新的概念:
1. 打印匹配的行
如果 未指定搜索模式,Awk 默认匹配输入文件中的所有行,并对每一行执行指定的操作。
2. 自定义输出
print
语句可以输出自定义文本。在这里,我们在输出的年份和描述字段之间插入了 四个空格:
print $3, " ",$5,$6,$7,$8
其中," "
代表四个空格。
3. 使用 if
语句
if
语句用于检查特定的条件,只有当条件满足时,print
语句才会执行:
if ($3 < 1980)
这里的条件 ($3 < 1980)
表示 如果第三个字段(年份)小于 1980,则执行 print
语句。
4. Awk 的数据类型
在大多数编程语言中,字符串和数字是不同的数据类型,它们的操作方式各不相同:
- 数字可以进行加法、减法等数学运算。
- 字符串可以进行拼接,但不能直接进行数学运算。
然而,在 Awk 中,字符串和数字之间的界限是模糊的。Awk 并不是“强类型”(strongly-typed)语言,它会自动将字符串转换为数值(如果可能的话)。因此,我们可以直接在 $3
(年份)字段上执行 数值比较,而不需要显式转换。
BEGIN 和 END
下面的示例统计文件中硬币的总数量:
awk 'END {print NR,"coins"}' coins.txt
输出:
13 coins
END 语句
END
语句用于执行文件处理完成后的操作。为了更好地理解 END
,需要介绍 Awk 程序的通用结构:
awk 'BEGIN { 初始化 } 搜索模式 1 { 操作 } 搜索模式 2 { 操作 } ... END { 结束操作 }' 输入文件
BEGIN {}
:在 Awk 读取文件之前执行初始化操作(可选)。搜索模式 { 操作 }
:处理匹配的行(可有多个)。END {}
:在 Awk 处理完所有行之后执行的操作(可选)。
在示例 awk 'END {print NR,"coins"}' coins.txt
中:
- NR 是 Awk 的一个内置变量,表示当前处理的行数(即记录数)。
- 由于
NR
代表文件中的总行数,因此END {print NR,"coins"}
在文件读取完毕后执行,并打印总行数 + "coins"。
Awk 内置变量
NR
:当前处理的行号(总行数)。NF
:当前行的字段数。
计算金币总价值
假设当前金价为 $425/盎司,我们希望计算金币的总价值:
awk '/gold/ {ounces += $2} END {print "value = $" 425*ounces}' coins.txt
输出:
value = $2592.5
变量 ounces
在这个例子中:
{ounces += $2}
在 C 语言 中,+=
是一种简写:
ounces = ounces + $2
计算金币总价值
END {print "value = $" 425*ounces}
print
语句的两个参数"value = $"
和425*ounces
之间没有逗号,因此它们会直接拼接,不添加额外的空格。
练习
如果不提供输入文件,Awk 允许用户手动输入数据,按 CTRL-D
退出。例如:
awk '{print "You entered:", $0}'
然后手动输入文本并查看输出。
练习题
-
修改上述程序,分别计算金币和银币的总重量(盎司)
- 你需要使用两个模式
/gold/
和/silver/
,并定义两个累加变量。
- 你需要使用两个模式
-
编写 Awk 程序,计算所有在 USA 铸造的硬币的平均重量
- 你需要统计符合条件的硬币数量,并计算平均值。
-
编写 Awk 程序,给每一行文本添加行号
- 例如,输入:
输出:Hello World This is Awk
1: Hello World 2: This is Awk
- 例如,输入:
在下一章,我们将学习如何编写多行 Awk 程序,从而实现更复杂的功能。