控制 awk 脚本流程的 4 种方法

学习使用 switch 语句以及 break、continue 和 next 命令来控制 awk 脚本。
84 位读者喜欢这篇文章。
JavaScript in Vim

Alex Sanchez. CC BY-SA 4.0.

有很多方法可以控制 awk 脚本的流程,包括循环switch 语句和 breakcontinuenext 命令。

示例数据

创建一个名为 colours.txt 的示例数据集,并将以下内容复制到其中

name       color  amount
apple      red    4
banana     yellow 6
strawberry red    3
raspberry  red    99
grape      purple 10
apple      green  8
plum       purple 2
kiwi       brown  4
potato     brown  9
pineapple  yellow 5

Switch 语句

switch 语句是 GNU awk 特有的功能,因此你只能将其与 gawk 一起使用。如果你的系统或目标系统没有 gawk,则不应使用 switch 语句。

gawk 中的 switch 语句类似于 C 和许多其他语言中的 switch 语句。语法是

switch (expression) {
	case VALUE:
		<do something here>
	[...]
	default:
		<do something here>
}

expression 部分可以是返回数字或字符串结果的任何 awk 表达式。 VALUE 部分(在 case 之后)是数字或字符串常量或正则表达式。

switch 语句运行时,会评估 *expression*,并将结果与每个 case 值进行匹配。 如果存在匹配项,则执行 case 定义中包含的代码。 如果任何 case 定义中都没有匹配项,则执行 default 语句。

关键字 break 位于每个 case 定义中的代码末尾,用于中断循环。 如果没有 break,awk 将继续搜索匹配的 case 值。

这是一个 switch 语句的示例

#!/usr/bin/awk -f
#
# Example of the use of 'switch' in GNU Awk.

NR > 1 {
    printf "The %s is classified as: ",$1

    switch ($1) {
        case "apple":
            print "a fruit, pome"
            break
        case "banana":
        case "grape":
        case "kiwi":
            print "a fruit, berry"
            break
		case "raspberry":
			print "a computer, pi"
			break
        case "plum":
            print "a fruit, drupe"
            break
        case "pineapple":
            print "a fruit, fused berries (syncarp)"
            break
        case "potato":
            print "a vegetable, tuber"
            break
        default:
            print "[unclassified]"
    }
}

这个脚本特别忽略了文件的第一行,在本例中,第一行只是一个标题。 它通过仅对索引号大于 1 的记录进行操作来实现这一点。 在所有其他记录上,此脚本将第一个字段 ($1,如你从之前的文章中了解到的) 的内容与每个 case 定义的值进行比较。 如果存在匹配项,则使用 print 函数打印条目的植物学分类。 如果没有匹配项,则 default 实例打印 “[未分类]”

香蕉、葡萄和猕猴桃在植物学上都被归类为浆果,因此有三个 case 定义与一个 print 结果相关联。

colours.txt 示例文件上运行该脚本,你应该得到以下结果

The apple is classified as: a fruit, pome
The banana is classified as: a fruit, berry
The strawberry is classified as: [unclassified]
The raspberry is classified as: a computer, pi
The grape is classified as: a fruit, berry
The apple is classified as: a fruit, pome
The plum is classified as: a fruit, drupe
The kiwi is classified as: a fruit, berry
The potato is classified as: a vegetable, tuber
The pineapple is classified as: a fruit, fused berries (syncarp)

Break

break 语句主要用于提前终止 forwhiledo-while 循环或 switch 语句。 在循环中,break 通常用在无法预先确定循环迭代次数的情况下。 调用 break 会终止封闭循环(这在存在嵌套循环或循环内的循环时很重要)。

此示例直接来自 GNU awk 手册,它展示了一种查找最小除数的方法。 阅读其他注释以清楚地理解代码的工作原理

#!/usr/bin/awk -f

{
    num = $1

    # Make an infinite FOR loop
    for (divisor = 2; ; divisor++) {

        # If num is divisible by divisor, then break
        if (num % divisor == 0) {
            printf "Smallest divisor of %d is %d\n", num, divisor
            break
        }

        # If divisor has gotten too large, the number has no
        # divisor, so is a prime 
        if (divisor * divisor > num) {
            printf "%d is prime\n", num
            break
        }
    }
}

尝试运行该脚本以查看其结果

    $ echo 67 | ./divisor.awk
    67 is prime
    $ echo 69 | ./divisor.awk
    Smallest divisor of 69 is 3

正如你所看到的,即使脚本以一个显式的 *infinite* 循环开始,并且没有结束条件,break 函数也能确保脚本最终终止。

Continue

continue 函数类似于 break。 它可以在 forwhiledo-while 循环中使用(尽管它与 switch 语句无关)。 调用 continue 会跳过封闭循环的其余部分,并开始下一个循环。

这是 GNU awk 手册中的另一个很好的示例,用于演示 continue 的可能用途

#!/usr/bin/awk -f

# Loop, printing numbers 0-20, except 5

BEGIN {
    for (x = 0; x <= 20; x++) {
        if (x == 5)
            continue
        printf "%d ", x
    }
    print ""
}

此脚本在打印任何内容之前分析 x 的值。 如果该值正好为 5,则调用 continue,导致跳过 printf 行,但循环保持未中断。 尝试使用相同的代码,但使用 break 来查看差异。

Next

此语句与 breakcontinue 等循环无关。 相反,next 适用于 awk 的主要记录处理周期:你放置在 BEGIN 和 END 函数之间的函数。 next 语句导致 awk 停止处理*当前输入记录*,并移至下一个记录。

如你从本系列之前的文章中了解到的,awk 从其输入流中读取记录并将规则应用于它们。 next 语句停止执行当前记录的规则,并移至下一个记录。

这是一个使用 next 在特定条件下“保留”信息的示例

#!/usr/bin/awk -f

# Ignore the header
NR == 1 { next }

# If field 2 (colour) is less than 6
# characters, then save it with its
#  line number and skip it

length($2) < 6 {
    skip[NR] = $0
    next
}

# It's not the header and
# the colour name is > 6 characters, 
# so print the line
{
    print
}

# At the end, show what was skipped
END {
    printf "\nSkipped:\n"
    for (n in skip)
        print n": "skip[n]
}

此示例在第一个规则中使用 next 来避免文件的第一行,即标题行。 第二个规则跳过颜色名称少于六个字符的行,但它也会将该行保存在一个名为 skip 的数组中,使用行号作为键(也称为 *索引*)。

第三条规则打印它看到的任何内容,但如果规则 1 或规则 2 导致跳过它,则不会调用该规则。

最后,在所有处理结束时,END 规则打印数组的内容。

在上面的 colours.txt 文件(以及之前的文章)上运行示例脚本

$ ./next.awk colours.txt
banana     yellow 6
grape      purple 10
plum       purple 2
pineapple  yellow 5

Skipped:
2: apple      red    4
4: strawberry red    3
6: apple      green  8
8: kiwi       brown  4
9: potato     brown  9

控制狂

总之,switchcontinuenextbreak 是 awk 规则的重要抢先异常,可以更好地控制你的脚本。 你不必直接使用它们;通常,你可以通过其他方式获得相同的逻辑,但它们是很好的便捷函数,可以使程序员的生活更轻松。 本系列的下一篇文章将介绍 printf 语句。


你更喜欢听这篇文章吗? 它改编自 Hacker Public Radio 的一集,这是一个由黑客为黑客制作的社区技术播客。

接下来要读什么
Seth Kenlon
Seth Kenlon 是一位 UNIX 极客、自由文化倡导者、独立多媒体艺术家和 D&D 爱好者。 他曾在电影和计算行业工作,通常是同时进行。

评论已关闭。

Creative Commons License本作品采用 Creative Commons Attribution-Share Alike 4.0 International License 许可。
© . All rights reserved.