04、AWK 工作流程

对于会的人来说,AWK 极其简单,对于不会的人来说,AWK 简直难如登天。

那是什么原因导致了两种极端?

想想,我们为什么如此的胆小,如此的担心受怕?

面对未知的事物,我们从来不敢越雷池一步,但对于会的已知的,我们却易如反掌。

是什么原因导致了两者的不同?

这个问题困惑我好久,直到有一天,我发现,对于会的已知的,我基本都知道它们是怎么工作的!

那么,对于 AWK,如果我们要熟练掌握,就必须知道它是怎么工作的,也就是它的工作流程。

AWK 工作流程

如果你想成为一个 AWK 专家,你必须知道 AWK 的内部是如何工作的。

AWK的工作流程,说起来真的很简单:

开始 -> 读取 -> 执行 -> 读取 -> 执行 -> .... -> 读取 -> 执行 -> 结束

读取

AWK从输入流中读取 一行,然后保存在内存中

这个输入流可以是 标准输入流,可以是一个 文件,还可以是一个管道

执行

执行就是把 BEGIN 和 END 之间代码按照顺序应用到刚刚读取的 一行 上。

默认情况下,针对输入流中的每一行,AWK 命令都会执行一次,当然了,我们可以使用一些 代码 来改变这种默认行为

重复

如果输入流中还存在未读取的行,那么会重复 读取 -> 执行,直到输入流中不存在行为止,也就是遇到 文件结束符 为止。

程序结构

对照上面这张图,我们来理一理 AWK 的程序结构

BEGIN 块

BEGIN 关键字表示 AWK 程序的开始,它的使用语法如下

BEGIN {awk-commands}

BEGIN 语句只会执行一次。

我们可以把一些要做的初始化工作放到 BEGIN 语句中,比如变量的初始化。

BEGIN 是 AWK 的一个关键字,而且必须 大写

需要注意的是: BEGIN 是可选的,如非必要,可以直接省略。

AWK 主体代码

AWK主体代码是 AWK 程序最重要的部分,AWK 会把从输入流中读取的每一行都应用到 AWK 主体代码

也就是说,对于输入流中的每一行,都会执行一次 AWK 主体代码

AWK主体代码有着如下的语法

/pattern/ {awk-commands}

AWK主体代码可以有一个模式 /pattern/ 开头,这个模式可以对将要执行的输入行进行一些限制和过滤。

对于AWK 主体代码,有三点需要注意:

1、 模式是夹在两个正斜杠(/)之间的;
2、 AWK主体代码没有任何关键字标识;
3、 AWK主体代码也是可选的,可以忽略;

END 块

END 关键字用于标识 AWK 程序的结束,通常用于处理一些结尾工作

它的语法结构如下

END {awk-commands}

END 和 BEGIN 一样都是 AWK 的关键字,而且都必须是大写的。

同样的END 块和 BEGIN 块一样,都是可选的。

范例

讲了那么多,我们用一段 AWK 代码来演示下刚刚我们学习的 AWK 工作流程和 AWK 程序结构

我们首先创建一个文件 employee.txt,它包含了我们公司雇员的一些信息

这个employee.txt 文件有着以下结构:

1)  张三  技术部  23
2)  李四  人力部  22
3)  王五  行政部  23
4)  赵六  技术部  24
5)  朱七  客服部  23

对照着上面的程序结构,我们来讲讲可以被 AWK 处理的程序的一般结构:

1、 每个雇员独占一行;
2、 每个雇员都包括以下字段:序号,名字,部门,年龄;
3、 每一行的多个字段之间使用空白作为分隔符,空白分隔符一般是空格()或者制表符\t;

然后,我们想要获得以下输入结果

序号 姓名  部门   年龄
-------------------
1)  张三  技术部  23
2)  李四  人力部  22
3)  王五  行政部  23
4)  赵六  技术部  24
5)  朱七  客服部  23
-------------------

分析

从上面的结果中可以看出,整个输出结果分为三大部分

1、 表头;

序号 姓名  部门   年龄
-------------------

2、 数据;

每个雇员一样,且有着以下的结构

1.  张三  技术部  23

3、 表尾;

-------------------

根据我们刚刚学到的知识,AWK 程序结构分为三大部分,BEGIN 和 END 只会执行一次,看起来刚好可以用来输出表头

而AWK 主体代码,是针对每一行执行的,因此,刚好可以用来输出数据。

表头

为了输入表头,我们可以使用下面的 Shell 语句

[www.ddkk.com]$ awk 'BEGIN{printf "序号\t名字\t部门\t年龄\n----------------------------\n"}'

因为表头部分不需要处理输入文件,也不需要处理输入文件中的每一行,所以 AWK 主体代码都是可以忽略的,甚至包括输入文件都是可以忽略的

运行上面的代码,输入结果如下

序号  名字  部门  年龄
---------------------------

数据

对比想要的结果和输入的文件,可以看出输出结果并并没有对每一行做特殊处理,直接显示就好

因此,只需要在 BEGIN 块后面直接添加 {print} ,添加完代码如下

[www.ddkk.com]$ awk 'BEGIN{printf "序号\t名字\t部门\t年龄\n----------------------------\n"}{print}'

print 用于输出当前行,现在不懂没关系,等下我们会详细介绍

当然了,如果直接这样运行,会发现程序被卡住了

为什么呢?

因为它卡在了读取一行数据这里,它在等待我们输入数据

为了提供输入数据,我们可以在整个 shell 命令后直接追加文件路径和文件名 employee.txt

添加完之后的 shell 代码如下

[www.ddkk.com]$ awk 'BEGIN{printf "序号\t名字\t部门\t年龄\n----------------------------\n"}{print}' employee.txt

运行上面的命令,输入结果如下

序号  名字  部门  年龄
----------------------------
1)  张三  技术部  23
2)  李四  人力部  22
3)  王五  行政部  23
4)  赵六  技术部  24
5)  朱七  客服部  23

表尾

最后,从想要的结果中可以看出,整个结果最后还有一行虚线,这个,我们可以通过 END 语句来实现

因为END 语句和 BEGIN 语句类似,我们就直接给结果了

[www.ddkk.com]$ awk 'BEGIN{printf "序号\t名字\t部门\t年龄\n----------------------------\n"}{print}END{printf "----------------------------\n"}' employee.txt

运行上面的命令,输出结果如下

序号  名字  部门  年龄
----------------------------
1)  张三  技术部  23
2)  李四  人力部  22
3)  王五  行政部  23
4)  赵六  技术部  24
5)  朱七  客服部  23
----------------------------