Author:onceday Date:2022年11月1日
全系列文章请查看专栏: Linux Shell基础_Once_day的博客-CSDN博客。
漫漫长路,有人对你微笑过嘛…
本文主要收集整理于以下文档:
gawk编程语言支持两种不同类型的变量:
gawk有一些内建变量,这些变量存放用来处理数据文件中的数据字段和记录的信息。也可以在gawk程序中创建自己的变量。
数据字段变量是gawk中的一种内建变量类型,即使用$n
来引用第n个位置的字段。如下:
$1
引用记录中的第一个数据字段。$2
引用第二个字段。数据字段是有字符分割符来划定的,默认情况下,字段分割符是一个空白字符,也就是空格符或者制表符。
下面是常见的数据字段和记录变量:
变量 | 描述 |
---|---|
FIELDWIDTHS | 由空格分割的一列数字,定义了每个数据字段确切宽度 |
FS | 输入字段分隔符 |
RS | 输入记录分隔符 |
OFS | 输出字段分隔符 |
ORS | 输出记录分隔符 |
下面是使用的例子:
onceday@ubuntu:shell$ cat data1.txt
data11,data12,data13,data14
data21,data22,data23,data24
data31,data32,data33,data34
onceday@ubuntu:shell$ gawk 'BEGIN{FS=","} {print $1,$2,$3}' data1.txt
data11 data12 data13
data21 data22 data23
data31 data32 data33
可以看到,输入的字段分隔符由FS
指定,而RS
默认为\n
,因此每一行为一个记录,每个记录按照,
来区分不同的字段,前三个字段即上述对应的文本。输出OFS
和ORS
都是默认字符,因此为[space]
和\n
。
onceday@ubuntu:shell$ gawk 'BEGIN{FS=",";OFS="-"} {print $1,$2,$3}' data1.txt
data11-data12-data13
data21-data22-data23
data31-data32-data33
onceday@ubuntu:shell$ gawk 'BEGIN{FS=",";OFS="-";ORS=";;;"} {print $1,$2,$3}' data1.txt
data11-data12-data13;;;data21-data22-data23;;;data31-data32-data33;;;onceday@ubuntu:shell$
FIELDWIDTHS
变量允许直接按照字段的宽度来读取指定文本,这是专门用于应对没有字段分割符的数据。
onceday@ubuntu:shell$ gawk 'BEGIN{FIELDWIDTHS="4 3 4 3 4 3 6"} {print $1,$2,$3,$4,$7}' data1.txt
data 11, data 12, data14
data 21, data 22, data24
data 31, data 32, data34
下面是一个复杂的具体情况:
Riley Mullen
123 Main Street
Chicago, IL 60601
(312)55-1234
对于这种情况,需要把FS
变量设为换行符,表明数据流中的每行都是一个单独的字段,每行上的所有数据都属于同一个字段。为了判断一个记录从何开始,可以把记录分割符RS
设置为“”
,即空字符串,因为gawk会把空白行当成空字符串,即记录分割符。
gawk提供了很多变量来辅助工作,比如提取shell环境的信息。
变量 | 描述 |
---|---|
ARGC | 当前命令行参数个数 |
ARGIND | 当前文件在ARGV中的位置 |
ARGV | 包含命令行参数的数组 |
CONVFMT | 数字的转换格式(printf语句),默认值为%.6 g |
ENVIRON | 当前shell环境变量及其值组成的关联数组 |
ERRNO | 当读取或关闭输出文件发生错误时的系统错误号 |
FILENAME | 用作gawk输入数据的数据文件的文件名 |
FNR | 当前数据文件中的数据行数 |
IGNORECASE | 设成非零值时,忽略gawk命令中出现的字符串的字符大小写 |
NF | 数据文件中的字段总数 |
NR | 已处理的输入记录数 |
OFMT | 数字的输出格式,默认值为%.6g |
RLENGTH | 由match函数所匹配的子字符串的长度 |
RSTART | 有match函数所匹配的子字符串的起始位置 |
其中ARGC
和ARGV
是从shell中获取的命令行参数,如下:
onceday@ubuntu:shell$ gawk 'BEGIN{print ARGC,ARGV[0],ARGV[1]}' data1.txt
2 gawk data1.txt
可以发现,在脚本中引用gawk变量,变量名前面不加美元符。
ENVIRON
变量使用关联数组来提取shell环境变量,关联数组用作数组的索引值,而不是竖直。
onceday@ubuntu:shell$ gawk 'BEGIN{print ENVIRON["HOME"]}' data1.txt
/home/onceday
使用方法类似于字典,可以将shell的环境变量提取出来。
NF
变量可以在不知道具体位置的情况下,指定记录中的最后一个数据字段。
需要注意的是,NF
是含有最后一个行数的值,因此需要结合字段变量来使用。
onceday@ubuntu:shell$ cat data1.txt
data11,data12,data13,data14
data21,data22,data23,data24
data31,data32,data33,data34
onceday@ubuntu:shell$ gawk 'BEGIN{FS=","} {print $1,$NF}' data1.txt
data11 data14
data21 data24
data31 data34
FNR
是当前数据文件中的已处理过的记录数,NR
是已处理过的记录总数,可能包含多个文件。
onceday@ubuntu:shell$ gawk 'BEGIN{FS=","} {print $1,"FNR="FNR,"NR="NR}' data1.txt data1.txt
data11 FNR=1 NR=1
data21 FNR=2 NR=2
data31 FNR=3 NR=3
data11 FNR=1 NR=4
data21 FNR=2 NR=5
data31 FNR=3 NR=6
可以清楚看到,FNR的值在处理第二个数据文件时被重置了,而NR变量则在处理第二个数据文件时,继续计数。
gawk允许你定义自己的变脸并在程序代码中使用,gawk自定义变量名可以是任意数目的字母、数字和下划线,但是不能以数字开头。并且,要记住gawk变量名区分大小写。
可以直接在脚本中给变量赋值:
onceday@ubuntu:shell$ gawk 'BEGIN{testing="this is a test";print testing}'
this is a test
也可以使用数学算式来处理数字值。
onceday@ubuntu:shell$ gawk 'BEGIN{x=4;x=x * 2 + 3;print x}'
11
gawk命令行上也可以直接给变量赋值。
onceday@ubuntu:shell$ cat script1
BEGIN{FS=","}
{print $n}
onceday@ubuntu:shell$ gawk -f script1 n=2 data1.txt
data12
data22
data32
但是有一个问题,这个变量值在BEGIN
部分不可用,因此需要添加额外的命令参数-v
。
onceday@ubuntu:shell$ gawk 'BEGIN{x=n; x=x * 2 + 3;print x}' n=5
3
onceday@ubuntu:shell$ gawk -v n=5 'BEGIN{x=n; x=x * 2 + 3;print x}'
13
gawk提供关联数组用于数组功能,它的索引值可以各种字符串,且每个索引字符串都需要能够唯一标识出赋给它的数据元素。
下面可以定义一个数组元素:
var[index] = element
var
是变量名,index是关联数组的索引值,element是数据元素值。
name["student1"] = "Mike"
name["student2"] = "Job"
name["student3"] = "Bob"
onceday@ubuntu:shell$ gawk 'BEGIN{name["st1"] = "bob";print name["st1"]}' data1.txt
bob
使用非常简单,使用存储时的索引值去取值即可,也可以当成数组来使用:
onceday@ubuntu:shell$ gawk 'BEGIN{
> var[1]=21
> var[2]=41
> var[3]=54
> var[4]=64
> print var[1]+var[2]
> }'
62
遍历数组变量需要使用for
语法,因为关联数组无法确定索引值是什么。
for (var in array)
{
statements
}
该语句每次循环时将关联数组array的下一个索引值赋给变量var,然后执行一遍statements
,重要的是记住这个变量中存储的索引值而不是数组元素值。
onceday@ubuntu:shell$ gawk 'BEGIN{
>var[1]=21
>var[2]=41
>var[3]=54
>var[4]=64
>for (key in var) {
>print "Index:",key," - value:",var[key]
>}
>}'
Index: 1 - value: 21
Index: 2 - value: 41
Index: 3 - value: 54
Index: 4 - value: 64
需要注意,并不能假设索引值key
能按固定的顺序来返回,但是key总会遍历完成。
删除数组变量需要使用以下语法:
delete array[index]
一旦从关联数组中删除了索引值,就没有办法再用它来提取元素值。
gawk支持正则表达式,包括BRE
基础模式和ERE
扩展模式。
onceday@ubuntu:shell$ cat data1.txt
data11,data12,data13,data14
data21,data22,data23,data24
data31,data32,data33,data34
onceday@ubuntu:shell$ gawk 'BEGIN{FS=","} /22/{print $1}' data1.txt
data21
gawk程序会用正则表达式对记录中所有的数据字段进行匹配,包括字段分割符。
匹配操作符(matching operator)允许将正则表达式限定在记录中的特定数据字段,匹配操作符是波浪线~
。
$1 ~ /^data/
$1
变量代表记录中的第一个数据字段。这个表达式会过滤出第一个字段以文本data开头的所有记录。
onceday@ubuntu:shell$ cat data1.txt
data11,data12,data13,data14
data21,data22,data23,data24
data31,data32,data33,data34
onceday@ubuntu:shell$ gawk 'BEGIN{FS=","} $1 ~ /data2/{print $0}' data1.txt
data21,data22,data23,data24
也可以使用!
符号来排除正则表达式的匹配:
$1 !~ /expression/
也可以在匹配模式中使用数学表达式,这个功能在匹配数据字段中的数字值时非常方便。
onceday@ubuntu:shell$ cat num.txt
12,47,875,58,658
542,587,589,5632,45
78,54,69,74,653
onceday@ubuntu:shell$ gawk 'BEGIN{FS=","} $2 > 100{print $0}' num.txt
542,587,589,5632,45
在数学表达式中可以使用常见的数学比较表达式:
x == y: 值x等于y
x <= y: 值x小于等于y
x < y : 值x小于y
x >= y: 值x大于等于y
x > y : 值x大于y
对于文本数据也可以使用匹配,但表达式必须完全匹配,数据和模式严格匹配:
onceday@ubuntu:shell$ cat num.txt
12,47,875,58,658
542,587,589,5632,45
78,54,69,74,653
onceday@ubuntu:shell$ gawk 'BEGIN{FS=","} $2 == "47"{print $0}' num.txt
12,47,875,58,658
if (condition)
statement1
需要注意,if语句的花括号不能和其他地方的花括号弄混,必须独立出来:
onceday@ubuntu:shell$ cat num.txt
12,47,875,58,658
542,587,589,5632,45
78,54,69,74,653
onceday@ubuntu:shell$ gawk 'BEGIN{FS=","} {if ($2 > 50) { print $0}}' num.txt
542,587,589,5632,45
78,54,69,74,653
if
是支持else
子语句的,单行上需要添加分号:
if (condition) {statement1}; else {statement2};
while语句为gawk程序提供了一个基本的循环功能,下面是while语句的格式。
while (condition)
{
statements
}
while循环允许遍历一组数据,并检查迭代的结束条件。
onceday@ubuntu:shell$ cat num.txt
12,47,875,58,658
542,587,589,5632,45
78,54,69,74,653
onceday@ubuntu:shell$ gawk 'BEGIN{FS="," };{
total = 0
i = 1
while (i < 4)
{
total += $i
i++
}
avg = total / 3
print "Average:",avg
}' num.txt
Average: 311.333
Average: 572.667
Average: 67
在while语句中也可以使用break
和continue
语句,允许从循环中跳出。
类似于while语句,但会在检查语句条件之前执行命令。
do {
statements
} while (condition)
该语句会在求值之前先执行一次,其他和while没有区别。
风格和C语言是一致的。
for (variable assignment; condition; iteration process)
onceday@ubuntu:shell$ gawk '
> BEGIN {FS=","}
> { total = 0
> for (i = 1; i < 4; i++)
> {
> total += $i
> }
> avg = total / 3
> print "Average:",avg
> }' num.txt
Average: 311.333
Average: 572.667
Average: 67
下面是printf命令格式:
printf "format string", var1, var2 ...
该命令的使用方法和C语言类似。如下形式:
%[modifier]control-letter
下面是可选的格式化指定符中的控制字母:
控制字母 | 描述 |
---|---|
c | 将一个数作为ASCII字符显示 |
d | 显示一个整数值 |
i | 显示一个整数值(跟d一样) |
e | 用科学计数法显示一个数 |
f | 显示一个浮点值 |
g | 用科学计数法或浮点数显示(选择较短的形式) |
o | 显示一个八进制 |
s | 显示一个文本字符串 |
x | 显示一个十六进制值 |
X | 显示一个十六进制值,但用大写字母A-F |
下面是实例:
onceday@ubuntu:shell$ gawk 'BEGIN{
> x = 10 * 100
> printf "The answer is: %e\n",x
> }'
The answer is: 1.000000e+03
除了上面所示的几种字符,还有3种修饰符可以进一部控制输出。
-
,指明在向格式化空间中放入数据时采用左对齐而不是右对齐。函数 | 描述 |
---|---|
atan2(x, y) | x/y的反正切,x和y以弧度为单位 |
cos(x) | x的余弦,x以弧度为单位 |
exp(x) | x的指数部分 |
int(x) | x的整数部分,去靠近零一侧的值 |
log(x) | x的自然对数 |
rand() | 比0大比1小的随机浮点值 |
sin(x) | x的正弦,x以弧度为单位 |
sqrt(x) | x的平方根 |
srand(x) | 为计算随机数指定一个种子值 |
函数 | 描述 |
---|---|
and(v1, v2) | 执行值v1和v2的按位与运算 |
compl(val) | 执行val的补运算 |
lshift(val,count) | 将值val左移count位 |
or(v1,v2) | 执行值v1和v2的按位或运算 |
rshift(val,count) | 将值val右移count位 |
xor(v1, v2) | 执行值v1和v2的按位异或运算 |
函数 | 描述 |
---|---|
asort(s [,d]) | 将数组按照数据元素值排序,可指定存在d中。索引值被替换成表示新的排序顺序的连续数字 |
asorti(s [,d]) | 按照数据元素值排序,可指定存在d中.索引值作为数据元素值,连续数字索引表示排序顺序 |
gensub(r, s, h [,t]) | 查找变量$0或者目标t,来匹配正则表达式r,如果h是一个以g或G开头的字符串,就用s替换掉匹配的文本。如果h是一个数字,它表示要替换掉第n处r匹配的地方。 |
gsub(r, s [,t]) | 查找变量$0或者目标字符串t,来匹配正则表达式r,如果找到了,就全部替换为字符串s。 |
index(s, t) | 返回字符串t在字符串s中的索引值,如果没有找到的话返回0 |
length([s]) | 返回字符串s的长度,如果没有指定的话,返回$0的长度 |
match(s,r [,a]) | 返回字符串s中正则表达式r出现位置的索引,如果指定了数组a,它会存储s中匹配正则表达式的那部分。 |
split(s,a,[,r]) | 将s用FS字符或正则表达式r分开放到数组a中,返回字段的总数 |
sprintf(format,variables) | 用提供的format和variable返回一个类似于print输出的字符串 |
sub(r,s [,t]) | 在变量$0或目标字符串t中查找正则表达式r的匹配,如果找到了,就用字符串s替换掉第一处匹配。 |
substr(s, i [,n]) | 返回s中从索引值i开始的n个字符组成的子字符串。如果未提供n,则返回s剩下的部分。 |
tolower(s) | 将s中的所有字符转换为小写 |
toupper(s) | 将s中的所有字符转换为大写 |
函数 | 描述 |
---|---|
mktime(datespec) | 将一个按YYYY MM DD HH MM SS [DST]格式指定的日期转换成时间戳值 |
strftime(format [, timestamp]) | 将当前时间的时间戳货timestamp转化格式化日期(采用shell函数date()的格式) |
systime() | 返回当前时间的时间戳 |
定义函数:
function name ([variables])
{
statements
}
可以将自己写的gawk函数保存在一个函数库中,然后如下方式使用:
gawk -f funclib -f script4 data1
下面是一个实例:
onceday@ubuntu:shell$ cat num.txt
12,47,875,58,658
542,587,589,5632,45
78,54,69,74,653
onceday@ubuntu:shell$ gawk '
function myprint()
{
printf "%-16s - %s\n", $1, $2
}
BEGIN{FS=","}
{
myprint()
}' num.txt
12 - 47
542 - 587
78 - 54