一、变量
gawk编程语言支持两种变量:内建变量和自定义变量。
1、内建变量
gawk使用内建变量来引用一些特殊的功能。
字段和记录分隔符变量
数据字段变量
此变量允许使用美元符号($)和字段在记录中的位置值来引用对应的字段。要引用记录中的第一个数据字段,就用变量 $1,要引用第二个数据字段,就用变量 $2,以此类推。
数据字段由字段分隔符划定。默认情况下,字段分隔符是一个空白字符:空格或制表符。可以通过使用命令行选项 -F ,或者使用特殊的内建变量 FS 修改字段分隔符。
FIELDWIDTHS | 由空格分隔的一列数字,定义了每个数据字段的确切宽度 |
FS | 输入字段分隔符 |
RS | 输入记录分隔符 |
OFS | 输出字段分隔符 |
ORS | 输出记录分隔符 |
默认情况下,gawk会将OFS变量的值设置为一个空格。print命令会自动将OFS变量的值置于输出的每个字段之间。
gawk 'BEGIN{FS=","} {print $1,$2,$3}' test2.txt
可以通过设置0FS变量,可以在输出中用任意字符串来分隔字段。
gawk 'BEGIN{FS=","; OFS="*"} {print $1,$2,$3}' test2.txt
FIELDWIDTHS变量可以不通过字段分隔符读取记录。一旦设置了此变量,gawk就会忽略FS变量,并根据提供的字段宽度计算字段。
gawk 'BEGIN{FIELDWIDTHS="2 4 5 3"} {print $1,$2,$3,$4}' test3.txt
变量RS和ORS定义了gawk对数据流中记录的处理方式。默认情况下,gawk会将RS和ORS设置为换行符。
默认的RS值表明,输入数据流中的每行文本就是一条记录。
包含地址和电话号码的数据中,占据了多行
可以把FS变量设置成换行符,这样就表明数据流中的每一行都是一个单独的字段,行内的所有数据都属于同一个数据字段。同时把RS变量设置成空字符串,然后在数据记录之间留一个空行。gawk就会把每一个空行视为记录分隔符。
gawk 'BEGIN{FS="\n"; RS=""} {print $1,$3}' test3.txt
gawk将文件中的每一行都视为一个字段,同时将空行作为记录分隔符。
数据变量
ENVIRON变量使用关联数组来提取shell环境变量,其中关联数组用文本而非数值作为数组索引。
如下所示,可以用这种方法从shell中提取任何环境变量的值,以供gawk脚本使用。
gawk '
BEGIN{
print ENVIRON["HOME"]
print ENVIRON["PATH"]
}'
变量FNR、NF和NR用于在gawk脚本中跟踪数据字段和记录。
变量NF可以让用户在不知道具体位置的情况下引用记录中的最后一个数据字段。NF变量含有数据文件中的最后一个字段的编号,在其前面加上美元符号,就把它用作字段变量。
gawk 'BEGIN{FS=":" ; OFS=":"} {print $1,$NF}' /etc/passwd
gawk 'BEGIN{FS=":" ; OFS=":"} {$NF}' /etc/passwd
注意看执行两条命令后打印出来的区别之处。
FNR变量包含当前数据文件中已处理过的记录数,NR变量包含已处理过的记录总数。
gawk 'BEGIN{FS=","}{print $1, "FNR="FNR}' test2.txt test2.txt
上述gawk脚本在命令行中指定了两个输入文件(都是test2.txt),此脚本会打印第一个字段和FNR变量的当前值。
gawk 'BEGIN{FS=","}{print $1, "FNR="FNR, "NR="NR}
END{print "这儿总共有",NR,"条记录被处理了"}'' test2.txt test2.txt
在gawk处理第二个数据文件时,FNR变量的值被重置了,而NR变量则继续计数。
因此,如果只使用一个数据文件作为输入,那么FNR和NR的值是相同的;如果使用多个数据文件作为输入,那么FNR的值会在处理每个数据文件时被重置,NR的值则会继续计数直到处理完所有的数据文件。
2、自定义变量
gawk自定义变量的名称由任意数量的字母、数字和下划线组成,但不能以数字开头。而且,其变量名区分大小写。
在脚本中给变量赋值
在gawk脚本中给变量赋值与给shell脚本中的变量赋值一样,都使用赋值语句。
gawk '
BEGIN{
test1="测试……"
print test1
}'
也可以保存数值或文本值:
gawk '
BEGIN{
test1="测试……"
print test1
test1=89
print test1
}'
还可以处理数学算式:
gawk 'BEGIN{x=7; x=x *9 + 10; print x}'
在命令行中给变量赋值
可以在不修改脚本代码的情况下改变脚本的行为。
BEGIN{FS=","}
{print $n}
gawk -f test.sh n=3 test2.txt
通过上述命令,可以显示文件中的第3个字段(n=3)。
使用命令行参数来定义变量值会产生一个问题:在设置过变量之后,这个值在脚本的BEGIN部分不可用。
BEGIN{print "开始值是",n; FS=","}
{print $n}
gawk -f test.sh n=3 test2.txt
使用 -v 选项解决该问题,它允许在BEGIN部分之前设定变量。它必须放在脚本代码之前。
gawk -v n=3 -f test.sh test2.txt
二、数组
gawk使用关联数组来提供数组的功能。与数字型数组不同,关联数组的索引可以是任意文本字符串,它用各种字符串来引用数组元素,每个索引字符串都必须能够唯一地标识出分配给它的数组元素。就跟其它语言中的哈希表、字典类似。
1、定义数组变量
使用标准赋值语句定义数组变量。
格式:var[index] = element
var:变量名;index:关联数组的索引值;element:数组元素值
gawk 'BEGIN{
capital["中国"] = "北京"
print capital["中国"]
}'
2、遍历数组变量
在gawk脚本中遍历关联数组,使用for语句的一种特殊形式。
for {var in array}
{
statements
}
此for语句会在每次循环时将关联数组array的下一个索引值赋给变量var,然后执行一遍statements。变量var中存储的是索引而不是数组元素值。
gawk 'BEGIN{
var["a"] = 10
var["h"] = 190
var["k"] = 78
var["t"] = "hello"
for (test in var)
{
print "索引:",test," - 值:", var[test]
}
}'
如下所示,索引值没有固定的返回顺序。
3、删除数组变量
从关联数组中删除数组元素要使用一个特殊的命令:
delete array[index]
delete命令会从关联数组中删除索引值及其相关的数组元素值。
gawk 'BEGIN{
var["a"] = 10
var["h"] = 190
var["k"] = 78
var["t"] = "hello"
for (test in var)
{
print "索引:",test," - 值:", var[test]
}
delete var["k"]
print "*****"
for (test in var)
{
print "索引:",test," - 值:", var[test]
}
}'
三、使用模式
gawk支持几种类型的匹配模式来过滤数据记录。比如,关键字BEGIN和END可以在读取数据流之前或之后执行命令的特殊模式。
1、正则表达式
可以使用基础正则表达式(BRE)或扩展正则表达式(ERE)来筛选数据。
在使用正则表达式时,它必须出现在与其对应脚本的左花括号前。
# 匹配含有字符串“11”的数据
gawk 'BEGIN{FS=","} /11/{print $1}' test2.txt
#匹配用作字段分隔符的逗号
gawk 'BEGIN{FS=","} /,d/ {print $1}' test2.txt
2、匹配操作符
匹配操作符(~)是一个很强大的工具,可以将正则表达式限制在记录的特定数据字段。可以指定匹配操作符、数据字段变量以及要匹配的正则表达式。
$1 ~ /^data/:此表达式会过滤出第一个数据字段以data开头的所有记录。
gawk 'BEGIN{FS=","} $2 ~ /^data3/{print $0}' test2.txt
匹配第2个数据字段要以“data3”开头的数据。
可以用它在文件中搜索特定的数据元素。
gawk -F: '$1 ~ /csb/{print $1, $NF}' /etc/passwd
上面的脚本命令会在第一个数据字段中查找文本“csb”,如果匹配成功,则打印出第一个数据字段和最后一个数据字段。
也可以使用 ! 符号来排除正则表达式的匹配:
$1 !~ /expression/
gawk -F: '$1 !~ /^csb/{print $1,$NF}' /etc/passwd
匹配第一个数据字段不是以“csb”开头的数据, 如果匹配成功,则打印出第一个数据字段和最后一个数据字段。
3、数学表达式
显示出所有属于root用户组(组ID为0)的用户。
下面的gawk脚本会检查记录中值为0的第四个字段。
gawk -F: '$4 == 0{print $1}' /etc/passwd
也可以对文本数据使用表达式,但跟正则表达式不同,表达式必须完全匹配。
gawk -F, '$1 == "data"{print $1}' test2.txt
gawk -F, '$1 == "data21"{print $1}' test2.txt
四、结构化命令
1、if 语句
gawk编程语言支持标准格式的 if-then-else 语句。
gawk '{
if ($1 > 30)
{
x = $1 * 2
print x
}
}' test3.txt
gawk '{
if ($1 < 30)
{
x = $1 * 2
print x
} else
{
x = $1 / 2
print x
}}' test3.txt
也可以在单行中使用else语句。
gawk '{if ($1 < 30) print $1 * 2; else print $1 / 2}' test3.txt
2、while语句
gawk '{
total = 0
i = 1
while (i <=3)
{
total += $i
i++
}
avg = total / 3
print "平均值是:", avg
}' test1.txt
还可以使用break语句和continue语句
gawk '{
total = 0
i = 1
while (i <= 3)
{
total += $i
if (i == 2)
break
i++
}
avg = total / 2
print "前两个数据的平均值是:", avg
}' test1.txt
3、do-while语句
do-while语句与while语句类似,但会在检查条件语句之前先执行命令。
gawk '{
total = 0
i = 1
do
{
total += $i
i++
} while (total < 200)
print total
}' test1.txt
4、for语句
gawk '{
total = 0
for (i = 1; i <=3; i++)
{
total += $i
}
avg = total /3
print "平均值是:",avg
}' test1.txt
五、格式化打印
和C语言编程一样,gawk中的格式化打印命令 printf 可以控制如何显示数据
格式:printf "format string, var1, var2
format string是格式化输出的关键,它会用文本元素和格式说明符来具体指定如何呈现格式化输出。格式说明符是一种特殊的代码,可以指明显示什么类型的变量以及如何显示。
1、控制字母
gawk脚本会将每个格式说明符作为占位符,供命令中的每个变量使用。第一个格式说明符对应列出第一个变量,第二个对应第二个变量,以此类推。
c | 将数字作为ASCII字符显示 |
d | 显示整数值 |
i | 显示整数值 |
e | 用科学计数法显示数字 |
f | 显示浮点值 |
g | 用科学计数法或浮点数显示(较短的格式优先) |
o | 显示八进制值 |
s | 显示字符串 |
x | 显示十六进制值 |
X | 用大写字母显示十六进制值 |
gawk 'BEGIN{
x = 10 * 10000
printf "结果是:%e\n", x
}'
2、修饰符
除了控制字母,还有3种修饰符可以进一步控制输出。
width
指定输出字段的最小宽度。如果输出短于这个值,则printf会将文本右对齐,并用空格进行填充。如果输出比指定的宽度长,则按实际长度输出。
prec
指定浮点数中小数点右侧的位数或者字符串中显示的最大字符数。
减号 -
指明格式化空间中的数据采用左对齐而非右对齐
在同一行中打印多个输出。
gawk 'BEGIN{FS=","} {printf "%s ", $1} END{printf "\n"}' test2.txt
使用修饰符格式化第一个字符串的值
gawk 'BEGIN{FS="\n"; RS=""} {printf "%10s %s\n", $1, $2}' test1.txt
使用printf处理浮点值
gawk '{
total = 0
for (i = 1; i <=3; i++)
{
total += $i
}
avg = total / 3
printf "平均值是:%8.1f\n",avg
}' test3.txt
%8.1f强制printf命令将浮点值近似到小数点后一位。
六、内置函数
gawk编程语言提供了一些内置函数用于执行一些常见的数学、字符串以及时间运算。
1、数学函数
atan(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) | 为计算随机数指定一个种子值 |
and(v1,v2) | 对v1和v2执行按位AND运算 |
comp1(v1) | 对v1执行补运算 |
lshift(val,count) | 将val左移count位 |
or(v1,v2) | 对v1和v2执行按位OR运算 |
rshift(val,count) | 将val右移count位 |
xor(v1,v2) | 对v1和v2执行按位XOR运算 |
产生较大随机整数的常见方法是综合运用rand()函数和int()函数:
x = int(10 * rand())
上述命令会返回一个0~9(包括0和9)的随机整数值。
2、字符串函数
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中。返回分割后的字段总数 |
sub(r,s [,t]) | 在变量$0或目标字符串t中查找匹配正则表达式r的部分。如果找到了,就用字符串s替换第一处匹配 |
substr(s,i [,n]) | 返回s中从索引 i 开始、长度为n的子串。如果未提供n,则返回s中剩下的部分 |
tolower(s) | 将s中的所有字符都转换成小写 |
toupper(s) | 将s中的所有字符都转换成大写 |
gawk 'BEGIN{x = "ning"; print toupper(x); print length(x)}'
3、时间函数
时间函数多用于处理日志文件。日志文件中一般含有需要进行比较的日期。通过将日期的文本表示形式转换成纪元时(自1970-01-01 00:00:00 UTC到现在的秒数),可以做到很容易就比较日期。
mktime(dataspec) | 将一个按YYYY MM DD HH MM SS [DST]格式指定的日期转换成时间戳 |
strftime(format [, timestamp]) | 将当前时间的时间戳或timestamp(如果提供了的话)转化成格式化日期(采用shell命令date的格式) |
systime() | 返回当前时间的时间戳 |
gawk 'BEGIN{
date = systime()
day = strftime("%A, %B %d, %Y", date)
print day
}'
七、自定义函数
可以在gawk脚本中自己创建函数。
1、定义函数
定义自己的函数,必须使用function关键字。
function name([variables])
{
statements
}
function myrand(limit)
{
return int(limit * rand())
}
2、使用自定义函数
定义函数时,它必须出现在所有代码块之前(包括BEGIN代码块)。
gawk '
function myrand(limit)
{
return int(limit * rand())
}
BEGIN{
x = myrand(8)
print x
}'
3、创建函数库
gawk提供了一种方法可以将多个函数放入单个库文件中。
首先,创建一个包含所有gawk函数的文件
# 函数库名:functionlib
function myrand(limit)
{
return int(limit * rand())
}
function myprint()
{
printf "%-16s - %s\n", $1, $4
}
function printthird()
{
print $3
}
接着,使用函数库时,只要创建好gawk脚本文件,然后在命令行中同时指定库文件和脚本文件即可。
# gawk脚本名 test
BEGIN{ FS="\n"; RS=""}
{
myprint()
}
然后执行下述命令即可。(-f选项不能和内联gawk脚本一起使用,所以要在同一命令行中使用多个 -f 选项。)
gawk -f functionlib -f test test2.txt