Linux | awk 特殊模式“BEGIN 和 END”

引言

本文[1],我们将介绍Awk的更多特性,特别是两个特殊的模式:BEGIN和END。

这些独特的功能在我们努力扩展和深入探索构建复杂Awk操作的多种方法时,将大有裨益。

实例

让我们从Awk系列的开篇回顾开始,回想一下,当我们启动这个系列时,我提到了运行Awk命令的通用语法是这样的:

代码语言:javascript
复制
# awk 'script' filenames  

在上面的语法中,Awk 脚本的形式如下:

代码语言:javascript
复制
/pattern/ { actions } 

在编写脚本时,你可能会用到模式,这通常是正则表达式。同时,你也可以将模式理解为特殊的BEGIN和END模式。基于此,我们可以按照以下形式来编写Awk命令:

代码语言:javascript
复制
awk '
  BEGIN { actions } 
  /pattern/ { actions }
  /pattern/ { actions }
            ……….
  END { actions } 
' filenames 

在Awk脚本中,如果用到了BEGIN和END这两个特殊模式,它们各自代表的含义如下:

BEGIN模式:指的是在读取任何输入行之前,Awk会先执行BEGIN下指定的所有操作一次。END模式:指的是在Awk程序退出之前,会执行END下指定的所有操作。含有这些特殊模式的Awk命令脚本的执行顺序大致如下:

当脚本执行到BEGIN模式时,BEGIN下的所有操作会被执行一遍,这发生在读取任何输入行之前。接着,Awk会读取一行输入并将其分解成不同的字段。然后,Awk会将指定的非特殊模式与输入行逐一进行匹配,一旦匹配成功,就会执行该模式下的所有操作。这个过程会针对所有已指定的模式重复进行。对于所有输入行,第二和第三步会重复执行。处理完所有输入行后,如果脚本中有END模式,那么Awk将执行END下的操作。在使用特殊模式进行Awk操作时,你应该始终牢记这个执行顺序,以期获得最佳操作效果。

以domains.txt文件中的Tecmint所拥有的域名列表为例:

代码语言:javascript
复制
news.tecmint.com
tecmint.com
linuxsay.com
windows.tecmint.com
tecmint.com
news.tecmint.com
tecmint.com
linuxsay.com
tecmint.com
news.tecmint.com
tecmint.com
linuxsay.com
windows.tecmint.com
tecmint.com
代码语言:javascript
复制
$ cat ~/domains.txt

View Contents of File

以这个示例为例,我们的目标是统计域名 "tecmint.com" 在文件 "domains.txt" 中出现的次数。为此,我们编写了一个简短的 shell 脚本,利用了变量、数值表达式和赋值运算符的相关知识,脚本的具体内容如下:

代码语言:javascript
复制
#!/bin/bash
for file in $@; do
        if [ -f $file ] ; then
                #print out filename
                echo "File is: $file"
                #print a number incrementally for every line containing tecmint.com 
                awk '/^tecmint.com/ { counter+=1 ; printf "%s\n", counter ; }' $file
        else
                #print error info incase input is not a file
                echo "$file is not a file, please specify a file." >&2 && exit 1
        fi
done
#terminate script with exit code 0 in case of successful execution 
exit 0

接下来,我们将在上述脚本中的Awk命令里应用BEGIN和END这两个特殊模式,具体如下:

我们会对脚本做出如下修改:

代码语言:javascript
复制
awk '/^tecmint.com/ { counter+=1 ; printf "%s\n", counter ; }' $file

>

awk ' BEGIN { print "The number of times tecmint.com appears in the file is:" ; }
/^tecmint.com/ { counter+=1 ; }
END { printf "%s\n", counter ; }
' $file

修改Awk命令后,整个shell脚本现在变成了如下形式:

代码语言:javascript
复制
#!/bin/bash
for file in $@; do
if [ -f $file ] ; then
#print out filename
echo "File is: $file"
#print the total number of times tecmint.com appears in the file
awk ' BEGIN { print "The number of times tecmint.com appears in the file is:" ; }
/^tecmint.com/ { counter+=1 ; }
END { printf "%s\n", counter ; }
' $file
else
#print error info incase input is not a file
echo "$file is not a file, please specify a file." >&2 && exit 1
fi
done
#terminate script with exit code 0 in case of successful execution
exit 0

执行上述脚本时,它会首先显示文件 "domains.txt" 的位置,随后执行Awk命令脚本。在读取文件中的任何输入行之前,BEGIN特殊模式会先帮助我们输出信息:“域名tecmint.com在文件中出现的次数为:”。

接下来,我们的正则表达式模式 /^tecmint.com/ 将与每一行输入行进行匹配,对于匹配的每一行,将执行操作 { counter+=1 ; },这个操作用于统计域名 "tecmint.com" 在文件中出现的次数。

最终,END模式将输出域名 "tecmint.com" 在文件中出现的总次数。

代码语言:javascript
复制
$ ./script.sh ~/domains.txt

总结来说,我们通过研究BEGIN和END这两个特殊模式的概念,进一步探索了Awk的更多功能。正如我之前提到的,这些Awk的功能将助力我们构建更为复杂的文本过滤操作。

Reference

[1]

Source: https://www.tecmint.com/learn-use-awk-special-patterns-begin-and-end/