开篇语:
在 Linux 命令中,
awk
是一个处理文件中的数据的高级工具,它能提供一个类编程环境来修改和重新组织文件中的数据。
特别说明:
GNU gawk
awk
既是一个命令,也是一种程序语言,它可以有不同的实现版本。在 Linux 系统中,
awk
的实现版本是 GNU gawk。在 shell 中执行awk
命令,实际执行的是gawk
命令。
如此截图:
所以下文中所说的gawk也就是我们常称呼的awk。
一、初始gawk
gawk 程序让流编辑迈上了一个新的台阶,它提供了一种编程语言而不只是编辑器命令。
gawk 功能概述:
- 定义变量来保存数据;
- 使用算术和字符串操作符来处理数据;
- 使用结构化编程概念(比如if-then语句和循环)来为数据处理增加处理逻辑;
- 通过提取数据文件中的数据元素,将其重新排列或格式化,生成格式化报告。
1.1 gawk 的命令格式
gawk的命令格式:
gawk options program file
- -F fs 指定行中划分数据字段的字段分隔符
- -f file 从指定的文件中读取程序
- -v var=value 定义gawk程序中的一个变量及其默认值
- -mf N 指定要处理的数据文件中的最大字段数
- -mr N 指定数据文件中的最大数据行数
- -W keyword 指定gawk的兼容模式或警告等级
命令行选项提供了一个简单的途径来定制 gawk 程序中的功能。我们会在探索 gawk 时进一步了解这些选项。gawk 的强大之处在于程序脚本。可以写脚本来读取文本行的数据,然后处理并显示数据,创建任何类型的输出报告。
1.2 从命令行读取程序脚本
命令解说:
如果你操作这个示例在你的服务器终端,你会发现,并没有任何结果输出,而当你随意输入一个数字或者字符串的时候会发现都会返回 同一个结果,这个原因就在于没有在命令行上指 定文件名,所以gawk程序会从STDIN接收数据。在运行这个程序时,它会一直等待从STDIN输入 的文本。 如果你输入一行文本并按下回车键,gawk会对这行文本运行一遍程序脚本。和sed编辑器一 样,gawk程序会针对数据流中的每行文本执行程序脚本。由于程序脚本被设为显示一行固定的文 本字符串,因此不管你在数据流中输入什么文本,都会得到同样的文本输出。
1.3 使用数据字段变量
gawk 的主要特性之一是其处理文本文件中数据的能力。它会自动给一行中的每个数据元素分配一个变量。默认情况下, gawk会将如下变量分配给它在文本行中发现的数据字段,而且在文本行中,每个数据字段都是通过字段分隔符划分的。gawk在读取一行文本时,会用预定义的字段分隔符划分每个数据字段。gawk中默认的字段分隔符是任意的空白字符。
数据字段释义:
- $0代表整个文本行;
- $1代表文本行中的第1个数据字段;
- $2代表文本行中的第2个数据字段;
- $n代表文本行中的第n个数据字段。
操作示例:
a.显示第1个数据字段的值
1.4 在脚本程序中使用多个命令
gawk 编程语言支持将多条 命令组合成一个正常的程序。要在命令行上的程序脚本中使用多条命令,只要在命令之间放个分号即可。
操作示例:
命令详解:
第一条命令会给字段变量 $4 赋值。第二条命令会打印整个数据字段。注意, gawk 程序在输出中已经将原文本中的第四个数据字段替换成了新值。
1.5 从文件中读取程序
gawk编辑器支持将程序存储到文件中,然后再在命令行中引用。
a 操作示例:
命令详解:
应用脚本文件只需要使用 -f 参数即可,此示例程序脚本会使用print命令输出/etc/passwd文件的主目录数据字段(字段变 量$6),以及userid数据字段(字段变量$1)。
程序文件中也可以指定多条命令。只需要一条命令放一行即可,不需要用分号。
b操作示例:
1.6 处理数据前运行脚本
gawk 允许指定程序脚本何时运行。默认情况下, gawk 会从输入中读取一行文本,然后针对该行的数据执行程序脚本。有时可能需要在处理数据前运行脚本,此时需要用到关键字BEGIN ,它会强制gawk 在读取数据前执行 BEGIN 关键字后指定的程序脚本。
可以看到此时我们没有输入文本内容同样可以输出内容,当然这种单一的使用是不常用的,工作中我们常常是结合其他命令,此时的名利格式是这样的:
命令解说:
此时我们在BEGIN脚本区域后用 { } 定义一个新的脚本区域,这个脚本区域的程序会相继执行。值得注意的是,不管后面有几个脚本区域,他们都需要包含在同一个单引号之中。
1.7 在处理数据后运行脚本
与BEGIN对应,END关键字允许指定一个程序脚本,gawk会在读完数据后执行它。
演示截图:
命令解说:
当 gawk 程序输出完文件所有内容后,执行 END 脚本中的命令。
小小趣味实战:
脚本内容:
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"
}
演示截图(结尾部分有做删节)
说明:
这个脚本定义了一个叫作FS的特殊变量。这是定义 字段分隔符的另一种方法。这样你就不用依靠脚本用户在命令行选项中定义字段分隔符了。
二 、gawk 变量的使用
gawk支持两种类型的变量:
- 内建变量
- 自定义变量
2.1 gawk 内建变量
gawk 程序使用内建变量来引用程序数据里的一些特殊功能。
2.1.1 字段和记录分隔符变量
数据字段变量:
引用记录中的第一个数据字段,就用变量$1 ;要引用第二个字段,就用 $2 ,依次类推。
- FIELDWIDTHS 由空格分隔的一列数字,定义了每个数据字段确切宽度
- FS 输入字段分隔符
- RS 输入记录分隔符
- OFS 输出字段分隔符
- ORS 输出记录分隔符
OFS和FS都 定义了 gawk如何处理数据流中的数据字段,区别在于,OFS是使用在 print命令的输出上。默认情况下, gawk 将OFS设成一个空格。
说明:
FIELDWIDTHS 变量定义了四个字段, gawk 依此来解析数据记录。每个记录中的数字串会根据已定义好的字段长度来分割。
B:核心说明
变量 RS 和 ORS 定义了 gawk 程序如何处理数据流中的字段。默认情况下, gawk 将 RS 和 ORS 设为换行符。默认的RS 值表明,输入数据流中的每行新文本就是一条新纪录。
示例截图:
命令解说:
该示例中 把 FS变量设置成换行符。这就表明数据流中的每行都是一个单独的字 段,每行上的所有数据都属于同一个字段。把 RS 变量设置成空字符串,然后在数据记录间留一个空白行。 gawk 会把每个空白行当作一个记录分隔符。
2.1.2 数据变量
常用变量如下:
- ARGC 当前命令行参数个数
- ARGIND 当前文件在ARGV中的位置
- ARGV 包含命令行参数的数组
- CONVFMT 数字的转换格式(参见printf语句),默认值为%.6 g
- ENVIRON 当前shell环境变量及其值组成的关联数组
- ERRNO 当读取或关闭输入文件发生错误时的系统错误号
- FILENAME 用作gawk输入数据的数据文件的文件名
- FNR 当前数据文件中的数据行数
- IGNORECASE 设成非零值时,忽略gawk命令中出现的字符串的字符大小写
- NF 数据文件中的字段总数
- NR 已处理的输入记录数
- OFMT 数字的输出格式,默认值为%.6 g
- RLENGTH 由match函数所匹配的子字符串的长度
- RSTART 由match函数所匹配的子字符串的起始位置
在上面的列表中有一些 shell 脚本编程中的变量。 ARGC 和 ARGV 变量允许从 shell 中获得命令行参数的总数以及它们的值。但这可能有点麻烦,因为gawk 并不会将程序脚本当成命令行参数的一部分。
操作示例:
说明:
ARGC变量表明命令行上有两个参数。这包括gawk命令和data1参数(记住,程序脚本并不算参数)。ARGV数组从索引0开始,代表的是命令。第一个数组值是gawk命令后的第一个命令行参数。
命令解说:
本例中gawk 程序的命令行定义了两个输入文件(两次指定的是同样的输入文件)。 这个脚本会打印第一个数据字段的值和FNR 变量的当前值。注意,当 gawk 程序处理第二个数据文件时,FNR 值被设回了 1 。
演示截图:
命令解说:
FNR 变量的值在 gawk 处理第二个数据文件时被重置了,而 NR 变量则在处理第二个数据文件时 继续计数。结果就是:如果只使用一个数据文件作为输入,FNR 和 NR 的值是相同的;如果使用多 个数据文件作为输入,FNR 的值会在处理每个数据文件时被重置,而 NR 的值则会继续计数直到处 理完所有的数据文件。
2.2 自定义变量
gawk 允许定义自己的变量在程序代码中使用。 gawk 自定义变量名可以是任意数目的字母、数字和下划线,但不能以数字开头。重要的是,要记住gawk 变量名区分大小写。
2.2.1 在脚本中给变量赋值
示例截图:
命令说明:
此示例中,a保存了前面赋予的值。
截图示例:
命令说明:
赋值语句还可以包含数学算式来处理数字值。
2.2.2. 在命令行上给变量赋值
gawk可以在 命令行来给程序中的变量赋值。允许在正常的代码之外赋值,即时改变 变量的值。
说明:
使用命令行参数来定义变量值会有一个问题。在你设置了变量后,这个值在代码的 BEGIN 部分不可用。此时需要用到 -v参数来解决这个问题。