52. awk 程序结构


典型的 awk 程序具有以下三个块。

BEGIN 块语法

BEGIN { awk-commands }

BEGIN 块在 awk 开始执行输入文件中所有行的 body 块之前仅在开始时执行一次。

  • BEGIN 块是打印报表标题和初始化变量的好地方。
  • BEGIN 块中可以有一个或多个 awk 命令。
  • 关键字 BEGIN 应以大写形式指定。
  • BEGIN 块是可选的。

body 块语法

/pattern/ {action}

输入文件中的每一行都会执行一次 body 块。

  • 如果输入文件有 10 条记录,则 body 块中的命令将执行 10 次(输入文件中的每条记录执行一次)。
  • body 块没有关键字。 我们之前讨论过模式和动作

END块语法

END { awk-commands }

在 awk 完成执行输入文件中所有行的 body 块之后,END 块仅在最后执行一次。

  • END块是打印报表页脚和进行任何清理活动的好地方。
  • END块中可以有一个或多个 awk 命令。
  • 关键字 END 应以大写形式指定。
  • END块是可选的。

awk 程序的工作流如下:

awk-workflow

以下简单示例显示了三个 awk 块的运行情况。

$ awk 'BEGIN { FS=":";print "---header---" } \
/mail/ {print $1} \
END { print "---footer---"}' /etc/passwd
---header---
mail
mailnull
---footer---
注意:当您的命令很长时,您可以在单行上键入,也可以通过在每行末尾指定\将其拆分为多行。 上面的示例分 3 行输入,第 1 行和第 2 行末尾有一个\

在上面的例子中:

  • 'BEGIN { FS=":";print "---header---" }是开始块,它设置字段分隔符变量FS(稍后详细介绍),并打印标题。 这仅在主体循环之前执行一次。
  • /mail/ {print $1}是主体循环,包含模式和操作。即,这将在输入文件中搜索关键字 "mail" 并打印第一个字段。
  • END { print "---footer---"}' 是结束块,用于打印页脚。
  • /etc/passwd是输入文件。 对该文件中的每条记录执行主体循环。

除了从命令行执行上面的简单示例之外,您还可以从文件中执行它。

首先,创建以下包含 BEGIN、body 和 END 循环的myscript.awk文件:

$ vi myscript.awk
BEGIN {
  FS=":"
  print "---header---"
}
/mail/ {
  print $1
}
END {
  print "---footer---"
} 

接下来,对输入文件/etc/passwd执行myscript.awk,如下所示:

$ awk -f myscript.awk /etc/passwd
---header---
mail
mailnull
---footer---

请注意 awk 脚本内的注释以#开头。 如果您正在编写复杂的 awk 脚本,请遵循最佳实践:在*.awk 文件中写入足够的注释,以便您稍后查看该文件时更容易理解。

以下是一些随机的简单示例,向您展示 awk 块的各种组合。

awk -F: '{ print $1 }' /etc/passwd
awk -F: 'BEGIN { printf "username\n------\n"} \
{ print $1 } \
END { print "------" }' /etc/passwd
awk -F: 'BEGIN { print "UID"} { print $3 }' /etc/passwd

仅指定 BEGIN 块是有效的 awk 语法。 当您不指定主体循环时,指定输入文件是没有意义的,因为只对输入文件中的行执行主体循环。 因此,当您想使用 awk 程序执行与文件处理无关的操作时,仅使用 BEGIN 块。 在下面的示例中,我们仅使用 BEGIN 块来解释一些 awk 编程组件的工作原理。 您可以将这个想法用于您认为合适的任何事情。

只有简单的 BEGIN

$ awk 'BEGIN { print "Hello World!" }'
Hello World! 

请注意,您可以指定多个输入文件。 如果指定两个输入文件,首先将对输入文件 1 中的所有行执行主体块,接下来将对输入文件 2 中的所有行执行主体块。

$ awk 'BEGIN { FS=":";print "---header---" } \
/mail/ {print $1} \
END { print "---footer---"}' /etc/passwd /etc/group
---header---
mail
mailnull
mail
mailnull
---footer---

注意:即使您指定了多个输入文件,BEGIN 块和 END 块也只会执行一次。