gawk 程序是 Unix 中的原始 awk 程序的 GNU 版本。
gawk 程序让流编辑器迈上了一个新的台阶,它提供了一种编程语言而不只是编辑器命令。在 gawk 编程语言中,你可以做下面的事情:
- 定义变量来保存数据;
- 使用算数和字符串操作符来处理数据;
- 使用结构化编程概念(比如 if-then 语句和循环)来为数据处理增加处理逻辑;
- 通过提取数据文件中的数据元素,将其重新排列或格式化,生成格式化报告。
gawk 程序的报告生成能力通常用来从大文本文件中提取数据元素,并将它们格式化成可读的报告。其中最完美的例子是格式化日志文件。在日志文件中找出错误行会很难,gawk 程序可以让你从日志文件中过滤出需要的数据元素,然后你可以将其格式化,使得重要的数据更易于阅读。
1. gawk 数据格式
gawk 程序的基本格式如下:
gawk options program file
选项 | 描述 |
---|---|
-F fs | 指定行中划分数据字段的字段分隔符 |
-f file | 从指定的文件中读取程序 |
-v var=value | 定义 gawk 程序中的一个变量及其默认值 |
-mf N | 指定要处理的数据文件中的最大字段数 |
-mr N | 指定数据文件中的最大数据行数 |
-W keyword | 指定 gawk 的兼容模式或警告等级 |
2. 从命令行读取程序脚本
gawk 程序脚本用一对花括号来定义。你必须将脚本命令放到两个花括号 {} 中。
由于 gawk 命令行假定脚本是单个文本字符串,因此需要将脚本放到单引号中。
$ gawk '{print "hello world!"}'
这个程序脚本定义了一个命令:print 命令,它会将文本打印到 STDOUT。如果尝试运行这个命令,你可能会有些失望,因为什么都不会发生。原因在于没有在命令行上指定文件名,所以 gawk 程序会从 STDIN 接收数据。在运行这个程序时,它会一直等待从 STDIN 输入的文本。
如果你输入一行文本并按下回车键,gawk 会对这行文本运行一遍程序脚本。跟 sed 编辑器一样,gawk 程序会针对数据流中的每行文本执行程序脚本,由于程序脚本被设为显示一行固定的文本字符串,因此不管你在数据流中输入什么文本,都会得到同样的文本输出。
$ gawk '{print "hello world!"}'
This is a test
hello world!
hello
hello world!
This is another test
hello world!
要终止这个 gawk 程序,你必须标明数据流已经结束了。bash shell 提供了一个组合键来生成 EOF(End-of-File)字符。Ctrl+D 组合键会在 bash 中产生一个 EOF 字符。这个组合键能够终止该 gawk 程序并返回到命令行界面提示符下。
3. 使用数据字段变量
gawk 的主要特性之一是其处理文本文件中数据的能力。它会自动给一行中的每个数据元素分配一个变量。默认情况下,gawk 会将如下变量分配给它在文本行中发现的数据字段:
- $0 代表整个文本行;
- $1 代表文本行中的第 1 个数据字段;
- $2 代表文本行中的第 2 个数据字段;
- $n 代表文本行中的第 n 个数据字段。
在文本行中,每个数据字段都是通过字段分隔符划分的。gawk 在读取一行文本时,会用预定义的字段分隔符划分每个数据字段。gawk 中默认的字段分隔符时任意的空白字符(例如空格或制表符)。
在下面的例子中,gawk 程序读取文本文件,只显示第 1 个数据字段的值。
$ cat data2.txt
One line of test text.
Two lines of test text.
Three lines of test text.
$
$ gawk '{print $1}' data2.txt
One
Two
Three
$
该程序用 $1 字段变量来仅显示每行文本的第 1 个数据字段。
如果你要读取采用了其他字段分隔符的文件,可以用 -F 选项指定。
$ gawk -F: '{print $1}' /etc/passwd
root
daemon
bin
sys
sync
games
man
lp
mail
[...]
这个简短的程序显示了系统中密码文件的第 1 个数据字段。由于 /etc/passwd 文件用冒号来分隔数字字段,因而如果要划分开每个数据元素,则必须在 gawk 选项中将冒号指定为字段分隔符。
4. 在程序脚本中使用多个命令
gawk 允许你将多条命令组合成一个正常的程序。要在命令行上的程序脚本中使用多条命令,只要在命令之间放个分号即可。
$ echo "My name is Rich" | gawk '{$4="Christine"; print $0}'
My name is Christine
$
第一条命令会给字段比变量 $4 赋值,第二条命令会打印整个数据字段。
也可以用次提示符一次一行地输入程序脚本命令。
$ gawk '{
> $4="Christine"
> print $0}'
My name is Rich
My name is Christine
$
5. 从文件中读取程序
$ cat script2.gawk
{print $1 "'s home directory is " $6}
$
$ gawk -F: -f script2.gawk /etc/passwd
root's home directory is /root
daemon's home directory is /usr/sbin
bin's home directory is /bin
sys's home directory is /dev
sync's home directory is /bin
games's home directory is /usr/games
man's home directory is /var/cache/man
lp's home directory is /var/spool/lpd
[...]
$
script2.gawk 程序脚本会再次使用 print 命令打印 /etc/passwd 文件的主目录数据字段(字段变量 $6) 以及 userid 数据字段(字段变量 $1)。
可以在程序文件中指定多条命令。要这么做的话,只要一条命令放一行即可,不需要分号。
$ cat script3.gawk
{
text = "'s home directory is "
print $1 text $6
}
$
$ gawk -F: -f script3.gawk /etc/passwd
root's home directory is /root
daemon's home directory is /usr/sbin
bin's home directory is /bin
sys's home directory is /dev
sync's home directory is /bin
games's home directory is /usr/games
man's home directory is /var/cache/man
lp's home directory is /var/spool/lpd
[...]
$
script3.gawk 程序脚本定义了一个变量来保存 print 命令中用到的文本字符串。注意,gawk 程序在引用变量值时并未像 shell 脚本一样使用 $.
6. 在处理数据前运行脚本
gawk 还允许指定程序脚本何时运行。默认情况下,gawk 会从输入中读取一行文本,然后针对该行的数据执行程序脚本。有时可能需要在处理数据前运行脚本,比如为报告创建标题。BEGIN 关键字就是用来做这个的。它会强制 gawk 在读取数据前执行 BEGIN 关键字后指定的程序脚本。
$ gawk 'BEGIN {print "Hello World!"}'
Hello World!
$
这次 print 命令会在读取数据前显示文本。但它在显示了文本后会快速退出,不等待任何数据。如果想使用正常的程序脚本处理数据,必须用另一个脚本区域来定义程序。
$ cat data3.txt
Line1
Line2
Line3
$
$ gawk 'BEGIN {print "The data3 File Contents:"} {print $0}' data3.txt
The data3 File Contents:
Line1
Line2
Line3
$
7. 在处理数据后运行脚本
与 BEGIN 关键字类似,END 关键字允许你指定一个程序脚本,gawk 会在读完数据后执行它。
$ gawk 'BEGIN {print "The data3 File Contents:"}
> {print $0}
> END {print "End of File"}' data3.txt
The data3 File Contents:
Line1
Line2
Line3
End of File
$
当 gawk 程序打印完文件内容后,它会执行 END 脚本中的命令。这是在处理完所有正常数据后给报告添加页脚的最佳办法。
$ cat script4.gawk
BEGIN {
print "The latest list of users and shells"
print " UserID \t Shell"
print "-------- \t -------"
FS=":"
}
{
print $1 " \t " $7
}
END {
print "This concludes the listing"
}
$
这个脚本用 BEGIN 脚本来为报告创建标题。它还定义了一个叫做 FS 的特殊变量。这是定义字段分隔符的另一种方法。这样你就不用依靠脚本用户在命令行选项中定义字段分隔符了。
$ gawk -f script4.gawk /etc/passwd
The latest list of users and shells
UserID Shell
-------- -------
root /bin/bash
daemon /usr/sbin/nologin
bin /usr/sbin/nologin
sys /usr/sbin/nologin
sync /bin/sync
[...]
sshd /usr/sbin/nologin
pollinate /bin/false
mary /bin/bash
This concludes the listing
$