字段和记录分隔符变量
数据字段变量允许使用美元符号($)和字段在记录中的位置值来引用对应的字段。因此,要引用记录中的第一个数据字段,就用变量$1;要引用第二个数据字段,就用$2,以此类推。
数据字段由字段分隔符划定。在默认情况下,字段分隔符是一个空白字符,也就是空格或者制表符。
有一组内建变量可以控制 gawk 对输入数据和输出数据中字段和记录的处理方式。gawk 数据字段和记录变量如下表:
变量 | 描述 |
---|---|
FIELDWIDTHS | 由空格分隔的一列数字, 定义了每个数据字段的确切宽度 |
FS | 输入字段分隔符 |
RS | 输入记录分隔符 |
OFS | 输出字段分隔符 |
ORS | 输出记录分隔符 |
变量 OFS 用于 print 命令的输出。在默认情况下, gawk 会将 OFS 变量的值设置为一个空格。
命令:print $1,$2,$3
输出:field1 field2 field3
print 命令会自动将 OFS 变量的值置于输出的每个字段之间。通过设置 OFS 变量,可以在输出中用任意字符串来分隔字段。
FIELDWIDTHS 变量可以不通过字段分隔符读取记录。有些应用程序并没有使用字段分隔符,而是将数据放置在记录中的特定列。在这种情况下,必须设定 FIELDWIDTHS 变量来匹配数据在记录中的位置。一旦设置了 FIELDWIDTHS 变量, gawk 就会忽略 FS 变量, 并根据提供的字段宽度来计算字段。
一定要记住,一旦设定了 FIELDWIDTHS 变量的值,就不能再改动了。这种方法并不适用于变长的数据字段。
变量 RS 和 ORS 定义了 gawk 对数据流中记录的处理方式。在默认情况下, gawk 会将 RS 和 ORS 设置为换行符。默认的 RS 值表明,输入数据流中的每行文本就是一条记录。
数据变量
更多的 gawk 内建变量
变量 | 描述 |
---|---|
ARGC | 命令行参数的数量 |
ARGIND | 当前处理的文件在 ARGV 中的索引 |
ARGV | 包含命令行参数的数组 |
CONVFMT | 数字的转换格式(参见 printf 语句),默认值为%.6g |
ENVIRON | 当前 shell 环境变量及其值组成的关联数组 |
ERRNO | 当读取或关闭输入文件发生错误时的系统错误号 |
FILENAME | 用作 gawk 输入的数据文件的名称 |
FNR | 当前数据文件中的记录数 |
IGNORECASE | 设成非 0 值时,忽略 gawk 命令中出现的字符串的大小写 |
NF | 数据文件中的字段总数 |
NR | 已处理的输入记录数 |
OFMT | 数字的输出显示格式。默认值为%.6g.,以浮点数或科学计数法显示,以较短者为准,最多使用 6 位小数 |
RLENGTH | 由 match 函数所匹配的子串的长度 |
RSTART | 由 match 函数所匹配的子串的起始位置 |
gawk 并不会将程序脚本视为命令行参数的一部分:
$ gawk 'BEGIN{print ARGC,ARGV[1]}' data1
2 data1
$
跟 shell 变量不同,在脚本中引用 gawk 变量时,变量名前不用加美元符号。
ENVIRON 使用关联数组来提取 shell 环境变量。关联数组用文本(而非数值)作为数组索引。
数组索引中的文本是 shell 环境变量名, 对应的数组元素值是 shell 环境变量的值。
$ gawk '
> BEGIN{
> print ENVIRON["HOME"]
> print ENVIRON["PATH"]
> }'
/home/rich
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
$
NF 变量可以让你在不知道具体位置的情况下引用记录中的最后一个数据字段。NF 变量含有数据文件中最后一个字段的编号。可以在 NF 变量之前加上美元符号,将其用作字段变量。
FNR 变量包含当前数据文件中已处理过的记录数, NR 变量则包含已处理过的记录总数。因此,如果只使用一个数据文件作为输入, 那么 FNR 和 NR 的值是相同的; 如果使用多个数据文件作为输入,那么 FNR 的值会在处理每个数据文件时被重置, NR 的值则会继续计数直到处理完所有的数据文件。
gawk 自定义变量的名称由任意数量的字母、数字和下划线组成,但不能以数字开头。gawk 变量名区分大小写。
在脚本中给变量赋值
$ gawk '
> BEGIN{
> testing="This is a test"
> print testing
> testing=45
> print testing
> }'
This is a test
45
$
在命令行中给变量赋值
$ cat script1
BEGIN{FS=","}
{print $n}
$ gawk -f script1 n=2 data1
data12
data22
data32
$ gawk -f script1 n=3 data1
data13
data23
data33
$
$ cat script2
BEGIN{print "The starting value is",n; FS=","}
{print $n}
$ gawk -f script2 n=3 data1
The starting value is
data13
data23
data33
$
$ gawk -v n=3 -f script2 data1
The starting value is 3
data13
data23
data33
$
var[index] = element
例如,capital["Illinois"] = "Springfield"
$ gawk 'BEGIN{
> capital["Illinois"] = "Springfield"
> print capital["Illinois"]
> }'
Springfield
$
for (var in array)
{
statements
}
delete array[index]
$ gawk 'BEGIN{
> var["a"] = 1
> var["g"] = 2
> for (test in var)
> {
> print "Index:",test," - Value:",var[test]
> }
> delete var["g"]
> print "---"
> for (test in var)
> print "Index:",test," - Value:",var[test]
> }'
Index: a - Value: 1
Index: g - Value: 2
---
Index: a - Value: 1
$
$ gawk 'BEGIN{FS=","} /11/{print $1}' data1
data11
$
$ gawk 'BEGIN{FS=","} /,d/{print $1}' data1
data11
data21
data31
$
$1 ~ /^data/
$ gawk 'BEGIN{FS=","} $2 ~ /^data2/{print $0}' data1
data21,data22,data23,data24,data25
$
$ gawk -F: '$1 ~ /rich/{print $1,$NF}' /etc/passwd
rich /bin/bash
$
$1 !~ /expression/
$ gawk -F: '$4 == 0{print $1}' /etc/passwd
root
$
$ gawk -F, '$1 == "data"{print $1}' data1
$
$ gawk -F, '$1 == "data11"{print $1}' data1
$ data11
gawk 编程语言支持常见的结构化编程命令。
if 语句
if (condition)
statement1
也可以写在一行中:
if (condition) statement1
if (condition) statement1; else statement2
while (condition)
{
statements
}
do
{
statements
} while (condition)
for( variable assignment; condition; iteration process)
$ gawk '{
> total = 0
> for (i = 1; i < 4; i++)
> {
> total += $i
> }
> avg = total / 3
> print "Average:",avg
> }' data5
Average: 128.333
Average: 137.667
Average: 176.667
$
使用格式化打印命令 printf。如果熟悉 C 语言编程, 那么 gawk 中 printf 命 令的用法也是一样,允许指定具体如何显示数据的指令。
printf 命令的格式如下:
printf "format string", var1, var2
格式说明符的格式如下:
%[modifier]control-letter
格式说明符的控制字母
控制字母 | 描述 |
---|---|
c | 将数字作为 ASCII 字符显示 |
d | 显示整数值 |
i | 显示整数值(和 d 一样) |
e | 用科学计数法显示数字 |
f | 显示浮点值 |
g | 用科学计数法或浮点数显示(较短的格式优先) |
o | 显示八进制值 |
s | 显示字符串 |
x | 显示十六进制值 |
X | 显示十六进制值,但用大写字母 A~F |
如果要显示一个字符串变量,可以用格式说明符%s。如果要显示一个整数值,可以用%d 或%i( %d 是十进制数的 C 语言风格显示方式)。如果要用科学计数法显示很大的值, 可以使用格式说明符%e。
除了控制字母,还有 3 种修饰符可以进一步控制输出。
注意,你需要在 printf 命令的末尾手动添加换行符,以便生成新行,否则,printf 命令会继续在同一行打印后续输出。
例子:
$ gawk 'BEGIN{FS="\n"; RS=""} {printf "%16s %s\n", $1, $4}' data2
Ima Test (312)555-1234
Frank Tester (317)555-9876
Haley Example (313)555-4938
$
$ gawk 'BEGIN{FS="\n"; RS=""} {printf "%-16s %s\n", $1, $4}' data2
Ima Test (312)555-1234
Frank Tester (317)555-9876
Haley Example (313)555-4938
$
printf "Average: %5.1f\n",avg
gawk 编程语言提供了不少内置函数,以用于执行一些常见的数学、字符串以及时间运算。
数学函数
gawk 数学函数
函数 | 描述 |
---|---|
atan2(x, y) | x/y 的反正切, x 和 y 以弧度为单位 |
cos(x) | x 的余弦, x 以弧度为单位 |
exp(x) | x 的指数 |
int(x) | x 的整数部分, 取靠近 0 一侧的值 |
log(x) | x 的自然对数 |
rand( ) | 比 0大且比 1 小的随机浮点值 |
sin(x) | x 的正弦, x 以弧度为单位 |
sqrt(x) | x 的平方根 |
srand(x) | 为计算随机数指定一个种子值 |
int() 函数会生成一个值的整数部分,但并不会四舍五入取近似值。它的做法更像其他编程语言中的 floor 函数,会生成该值和 0 之间最接近该值的整数。
rand()函数会返回一个随机数,但这个随机数只在 0 和 1 之间(不包括 0 或 1)。要得到更大的数,就需要放大返回值。产生较大随机整数的常见方法是综合运用函数 rand()和 int()创建一个算法:
x = int(10 * rand())
在使用一些数学函数时要小心,因为gawk 编程语言对于其能够处理的数值有一个限定区间。如果超出了这个区间,就会得到一条错误消息:
$ gawk 'BEGIN{x=exp(1000); print x}'
gawk: warning: exp argument 1000 is out of range
inf
$
gawk 还支持一些按位操作数据的函数。
gawk 字符串函数
函数 | 描述 |
---|---|
asort(s [,d]) | 将数组 s 按照数组元素值排序。索引会被替换成表示新顺序的连续数字。另外,如果指定了 d,则排序后的数组会被保存在数组 d 中 |
asorti(s [,d]) | 将数组 s 按索引排序。生成的数组会将索引作为数组元素值,用连续数字索引表明排序顺序。另外,如果指定了 d,则排序后的数组会被保存在数组 d 中 |
gensub(r, s, h [,t]) | 针对变量$0或目标字符串 t(如果提供了的话)来匹配正则表达式 r。如果 h 是一个以 g 或 G 开头的字符串,就用 s 替换匹配的文本。如果 h 是一个数字,则表示要替换 r 的第 h 处匹配 |
gsub(r, s [,t]) | 针对变量$0或目标字符串 t(如果提供了的话) 来匹配正则表达式 r。如果找到了,就将所有的匹配之处全部替换成字符串 s |
index(s, t) | 返回字符串 t 在字符串 s 中的索引位置;如果没找到,则返回 0 |
length([s]) | 返回字符串 s 的长度;如果没有指定,则返回$0 的长度 |
match(s, r [,a]) | 返回正则表达式 r 在字符串 s 中匹配位置的索引。如果指定了数组 a,则将 s 的 匹配部分保存在该数组中 |
split(s, a [,r]) | 将 s 以 FS(字段分隔符)或正则表达式 r(如果指定了的话)分割并放入数组 a 中。返回分割后的字段总数 |
sprintf(format, variables) | 用提供的 format 和 variables 返回一个类似于 printf 输出的字符串 |
sub(r, s [,t]) | 在变量$0 或目标字符串 t 中查找匹配正则表达式 r 的部分。如果找到了,就用字符串 s 替换第一处匹配 |
substr(s, i [,n]) | 返回 s 中从索引 i 开始、长度为 n 的子串。如果未提供 n,则返回 s 中剩下的部分 |
tolower(s) | 将 s 中的所有字符都转换成小写 |
toupper(s) | 将 s 中的所有字符都转换成大写 |
asort 和 asorti 是新加入的 gawk 函数,允许基于数据元素值(asort)或索引(asorti)对数组变量进行排序。这里有个使用 asort 的例子:
$ gawk 'BEGIN{
> var["a"] = 1
> var["g"] = 2
> var["m"] = 3
> var["u"] = 4
> asort(var, test)
> for (i in test)
> print "Index:",i," - value:",test[i]
> }'
Index: 4 - value: 4
Index: 1 - value: 1
Index: 2 - value: 2
Index: 3 - value: 3
split 函数是将数据字段放入数组以供进一步处理的好办法:
$ gawk 'BEGIN{ FS=","}{
> split($0, var)
> print var[1], var[5]
> }' data1
data11 data15
data21 data25
data31 data35
$
gawk 的时间函数
函数 | 描述 |
---|---|
mktime(datespec) | 将一个按 YYYY MM DD HH MM SS [DST]格式指定的日期转换成时间戳 |
strftime(format [, timestamp]) | 将当前时间的时间戳或 timestamp (如果提供了的话)转化为格式化日期 (采用 shell 命令 date 的格式) |
systime() | 返回当前时间的时间戳 |
时间戳( timestamp )是自 1970-01-01 00:00:00 UTC 到现在,以秒为单位的计数,通常称为纪元时(epoch time)。systime()函数的返回值也是这种形式。
时间函数多用于处理日志文件。日志文件中通常含有需要进行比较的日期。通过将日期的文本表示形式转换成纪元时(自 1970-01-01 00:00:00 UTC 到现在的秒数),可以轻松地比较日期。
在 gawk 脚本中使用时间函数的例子:
$ gawk 'BEGIN{
> date = systime()
> day = strftime("%A, %B %d, %Y", date)
> print day
> }'
Friday, December 26, 2014
$
除了 gawk 中的内建函数, 还可以在 gawk 脚本中创建自定义函数。
定义函数
function name([variables])
{
statements
}
function myrand(limit)
{
return int(limit * rand())
}
x = myrand(100)
$ gawk '
> function myprint()
> {
> printf "%-16s - %s\n", $1, $4
> }
> BEGIN{FS="\n"; RS=""}
> {
> myprint()
> }' data2
Ima Test - (312)555-1234
Frank Tester - (317)555-9876
Haley Example - (313)555-4938
$
$ cat funclib
function myprint()
{
printf "%-16s - %s\n", $1, $4
}
function myrand(limit)
{
return int(limit * rand())
}
function printthird()
{
print $3
}
$
$ cat script4
BEGIN{ FS="\n"; RS=""}
{
myprint()
}
$ gawk -f funclib -f script4 data2
Ima Test - (312)555-1234
Frank Tester - (317)555-9876
Haley Example - (313)555-4938
$