Awk 入门/搜索模式 (2)
Awk 的字符串搜索功能还有更多内容。搜索可以限制在输入行中的单个字段。例如
$1 ~ /^France$/
这将搜索第一个字段($1
- 稍后将详细介绍“字段变量”)是“France”的行,而
$1 !~ /^Norway$/
这将搜索第一个字段不是“Norway”的行。
可以使用一个搜索模式匹配块中的第一行,另一个搜索模式匹配块中的最后一行,来搜索文本中的一系列或“块”连续行。例如
/^Ireland/,/^Summary/
这将匹配第一行以“Ireland”开头,最后一行以“Summary”开头的文本块。
工作原理如下:一旦匹配了/^Ireland/
,所有后续的文本行将自动匹配,直到匹配了/^Summary/
。此时,匹配停止。如果未找到以“Summary”开头的行,则从“Ireland”之后的文本到文件末尾的所有内容都将匹配。
搜索模式不必是正则表达式。它也可以是各种其他表达式。例如
NR == 10
这将匹配第 10 行。从 1 开始对行进行编号。NR
如概述中所述,是 Awk 搜索的行的计数,而 ==
是“等于”运算符。类似地
NR == 10,NR == 20
这将匹配输入文件中的第 10 行到第 20 行。
Awk 支持使用全面的比较运算符进行搜索模式
<
小于<=
小于或等于==
等于!=
不等于>=
大于或等于>
大于~
匹配!~
不匹配
例如,
NF == 0
这将匹配所有空行,或字段数为零的行。
$1 == "France"
这是一个字符串比较,它将匹配第一个字段是字符串“France”的任何行。细心的读者可能注意到,此示例似乎与上一个示例做的事情相同
$1 ~ /^France$/
实际上,这两个示例都做着同样的事情,但在上面的示例中,^
和 $
元字符必须用于正则表达式以指定与整个第一个字段的匹配;如果没有它们,它将匹配诸如“FranceFour”、“NewFrance”之类的字符串。字符串表达式只匹配“France”。
还可以使用 &&
(AND)和 ||
(OR)运算符组合多个搜索模式。例如
((NR >= 30) && ($1 == "France")) || ($1 == "Norway")
这将匹配第 30 行之后的任何以“France”开头的行,或任何以“Norway”开头的行。如果一行以“France”开头,但它在第 30 行之前,则它将不匹配。然而,所有以“Norway”开头的行将匹配。
上面未列出的一种模式匹配类别是对字段变量执行数字比较。当然可以做到;例如
$1 == 100
这将匹配第一个字段的数字值等于 100 的任何行。这很容易做到,并且可以正常工作。但是,假设我们要执行
$1 < 100
这通常可以正常工作,但有一个棘手的问题,需要进行一些解释:如果输入的第一个字段可以是数字或文本字符串,则这种数字比较可能会产生疯狂的结果,匹配一些与数字值不匹配的文本字符串。
这是因为 Awk 是一种弱类型语言。它的变量可以存储数字或字符串,Awk 将对每个变量执行相应的操作。在上面的数字比较中,如果$1
包含数字值,Awk 将按预期对其执行数字比较;但如果$1
包含文本字符串,Awk 将对$1
中的文本字符串和三字母文本字符串“100
”执行文本比较。对于简单地测试相等或不相等,这可以正常工作,因为数字和字符串比较将产生相同的结果,但对于“小于”或“大于”比较,它将产生意外的结果。本质上,在比较字符串时,Awk 会比较它们的 ASCII 值。这大致相当于按字母顺序(“电话簿风格”)排序。即使如此,它也不是完全按字母顺序排序,因为大写和小写字母将无法正确比较,数字和标点符号的比较方式有些随意。
底线:如果你不确定数据是字符串还是数字,只使用相等、不相等或正则表达式匹配。 |
Awk 并没有坏掉;它只是按照指示在这个例子中这样做。如果出现这个问题,可以向比较中添加第二个测试以确定字段是否包含数字值或文本字符串。第二个测试的形式为
(( $1 + 0 ) == $1 )
如果$1
包含数字值,则此表达式的左侧将向其添加 0,Awk 将执行始终为真的数字比较。
如果 $1 包含看起来不像数字的文本字符串,为了更好地做,Awk 将将其值解释为 0。这意味着表达式的左侧将计算为零;因为$1
中有一个非数字文本字符串,所以 Awk 将执行始终为假的字符串比较。这将导致更可行的比较
((( $1 + 0 ) == $1 ) && ( $1 > 100 ))
可以修改相同的测试来检查文本字符串而不是数字值
(( $1 + 0 ) != $1 )
值得记住这个技巧,以备不时之需。弱类型语言很方便,但在某些特殊情况下,它们可能会适得其反。
顺便说一句,如果对 Awk 处理特定类型数据的方式存在疑问,只需运行测试即可确定。例如,我想看看我的 Awk 版本是否可以处理 C 中指定的十六进制值 - 例如,“0xA8” - 所以我只需在命令提示符下输入以下内容
awk 'BEGIN {tv="0xA8"; print tv,tv+0}'
这打印了“0xA8 0”,这意味着 Awk 认为数据严格来说是一个字符串。这个小程序只包含一个 BEGIN
子句,允许在不指定输入文件的情况下运行 Awk 程序。这种“单行程序”在玩示例时很方便。如果你不确定 Awk 可能会做什么,只需尝试一下;它不会破坏任何东西。
- 编写一个 Awk 程序,打印任何包含少于 5 个单词的行,除非该行以星号开头。
- 编写一个 Awk 程序,打印每个以数字开头的行。
- 编写一个 Awk 程序,扫描带行号的文本文件以查找错误。它应该打印出任何缺少行号的行,以及任何编号错误的行,以及实际的行号。
在下一页中,您将了解字符串和数字的一些更细微的方面。