SHELL脚本学习(十四)gawk进阶

一、使用变量

gawk支持两种变量

  • 内建变量
  • 自定义变量
1.1 内建变量
1.1.1 字段和记录分隔符变量

数据字段变量允许使用美元符号 $ 和 位置来引用对应的字段。 $1 对应第一个数据字段,$2对应第二个数据字段,以此类推。

数据字段用字段分隔符划定。默认情况下,字段分隔符是一个空白字符(空格或制表符)。可以通过 -F选项修改字段分隔符。也可以使用特殊的内建变量FS修改字段分隔符。

有一组内建变量可以控制输入和输出数据中字段和记录的处理方式:

gawk数据字段和记录变量

变 量描 述
FIELDWIDTHS由空格分隔的一串数字,定义了每个数据字段的确切宽度
FS输入字段分隔符
RS输入记录分隔符
OFS输出字段分隔符
ORS输出记录分隔符
$ cat < data1
header line
data line 1
End of data line

#默认情况
$ gawk '{print $1,$2}' data1
header line
data line
End of

#1、OFS测试

$ gawk 'BEGIN{OFS="|-|"}
{print $1,$2}' data1
header|-|line
data|-|line
End|-|of

#2、ORS测试

$ gawk 'BEGIN{ORS="|-|"}
{print $1,$2}
END{print "\n"}' data1 
header line|-|data line|-|End of|-|

默认输出字段分隔符是空格,第1个例子将输出字段分隔符换成了"|-|“。
默认输出记录分隔符是换行,第2个例子将输出记录分隔符换成了”|-|"。

FIELDWIDTHS会根据提前设置好的字段宽度来划分字段。

下面这个例子将固定格式的时间划分成 年、月、日、时、分

$ cat <data2
202401011015
202402020900
202403030130

$ gawk 'BEGIN{
FIELDWIDTHS="4 2 2 2 2 2"}
{print $1,$2,$3,$4,$5}' data2
2024 01 01 10 15 
2024 02 02 09 00 
2024 03 03 01 30 

FS默认为空白符,RS默认为换行符。也就是说gawk默认一行为一条记录。但有时一行数据是一个字段,多行数据组成一条记录。这时可以将FS设置成\n,将RS设置成空字符串。

$ cat <data3
zhangsan
17
haerbin

lisi
20
beijing

$ gawk 'BEGIN{FS="\n";RS=""}
{print"name:"$1,"age:"$2,"city:"$3}' data3
name:zhangsan age:17 city:haerbin
name:lisi age:20 city:beijing

1.1.2 数据变量

除了字段和记录分隔符变量外,gawk还提供了其他一些变量帮助了解数据的变化。

更多的gawk内建变量

变量描述
ARGC命令行参数的数量
ARGIND当前处理的文件在ARGV中的索引
ARGV包含命令行参数的数组
CONVFMT数字的转换格式(参见printf语句),默认为%.6g
ENVIRON当前环境变量及其值组成的关联数组
ERRNO当读取或关闭输入文件发生错误时的系统错误号
FILENAME用作gawk输入的数据文件的名称
FNR当前数据文件中已处理的记录数
IGNORECASE非0表示忽略大小写
NF数据文件中的字段总数
NR已处理的输入记录数
OFMT数字的输出格式。默认值为%.6g。以浮点数或科学计数法表示,以较短者为准,最多是使用6位小数
RLENGTH由match函数所匹配的子串长度
RSTART由match函数所匹配的子串的起始位置

下面测试几个常用的变量

#ARGC:命令行参数的数量
#ARGV:当前处理的文件在ARGV中的索引
$ gawk 'BEGIN{print ARGC,ARGV[0],ARGV[1],ARGV[2]}' data1 data2
3 gawk data1 data2

#ENVIRON:当前环境变量及其值组成的关联数组
$ gawk 'BEGIN{print ENVIRON["HOME"]}' 
/home/ubuntu

#FILENAME :用作gawk输入的数据文件的名称
$ gawk 'END{print FILENAME}' data1
data1
$ cat < data1
header line
data line 1
End of data line

$ cat <data2
202401011015
202402020900
202403030130

#NR:已处理的输入记录数
#FNR:当前数据文件中已处理的记录数
#NF:数据文件中的字段总数
#$NF:最后一个字段的值
$ gawk '{print "NR="NR,"FNR="FNR,"NF="NF,"$NF="$NF}' data1 data2
NR=1 FNR=1 NF=2 $NF=line
NR=2 FNR=2 NF=3 $NF=1
NR=3 FNR=3 NF=4 $NF=line
NR=4 FNR=1 NF=1 $NF=202401011015
NR=5 FNR=2 NF=1 $NF=202402020900
NR=6 FNR=3 NF=1 $NF=202403030130
NR=7 FNR=4 NF=0 $NF=

1.2 自定义变量

gawk的自定义变量由任意个字母、数字和下划线组成,但不能以数字开头。
gawk自定义变量区分大小写。

1.2.1 在脚本中为变量赋值
$ gawk 'BEGIN{test="this is a test.";print test}'
this is a test.

$ gawk 'BEGIN{test=100;print test*(test-1)}'
9900

$ gawk 'BEGIN{test=100;print test^2}'
10000
1.2.2 在命令行中给变量赋值
$ cat <data1
header line
data line 1
End of data line

$ gawk '{print $field}'  field=1 data1
header
data
End

$ gawk '{print $field}'  field=2 data1
line
line
of

这个例子通过在命令行中给变量赋值,可以显示不同的字段。
这个特性可以在不改变脚本代码的情况下改变脚本的行为。

使用命令行参数定义变量有一个问题:设置变量后,这个变量在BEGIN模块不可用
如下:

$ gawk 'BEGIN {print "field="field}' field=3 data1
field=

可以用 -v选项解决这个问题。-v选项允许在BEGIN之前定义变量。

$ gawk -v field=3 'BEGIN {print "field="field}' field=3 data1
field=3
二、处理数组

数组用于单个变量存储多个值,gawk语言使用关联数组提供数组功能。类似c++中的unorder_map。

2.1 定义数组变量

可以用赋值语句定义数组变量。格式如下;

var[index]=element
var:数组变量名
index:索引
element:索引对应的值

$ gawk 'BEGIN{arr["name"]="lilei";arr["age"]=20;
print arr["name"],arr["age"]}'
lilei 20
2.2 遍历数组变量

格式:

for index in array
{
 statement
}

for语句每次循环时会把下一个数组元素的索引赋值给index。

$ gawk 'BEGIN{arr["name"]="lilei";arr["age"]=20;
for (ind in arr)
{
print "arr["ind"]="arr[ind]
}}'
arr[age]=20
arr[name]=lilei

索引没有固定的返回顺序。

2.3 删除数组变量

从关联数组中删除数据需要使用 delete 命令;

delete array[index]

下面使用delete命令的例子

$ cat < gawk_cmd
BEGIN {
    arr["name"]="lilei";
    arr["age"]=20;
    arr["city"]="haerbin";
    for (ind in arr)
    {
        print "arr["ind"] :" arr[ind];
    }
    delete arr["age"];
    print "---------"
    for (ind in arr)
    {
        print "arr["ind"] :" arr[ind];
    }
}

$ gawk -f gawk_cmd
arr[age] :20
arr[city] :haerbin
arr[name] :lilei
---------
arr[city] :haerbin
arr[name] :lilei

每行代码后面的分号 ( ; ) 写不写都行。

三、 使用模式
3.1 正则表达式

sed 只支持基础正则表达式(BRE),gawk支持基础正则表达式(BRE) 和扩展正则表达式(ERE)。

下面的第1个例子输出张三的年龄,第2个例子输出年龄在20~30之间的人的名字。

$ cat <data3
zhangsan
17
haerbin

lisi
20
beijing

$ gawk 'BEGIN{FS="\n";RS=""}
/zhangsan/{print $2}' data3
17

$ gawk 'BEGIN{FS="\n";RS=""}
/2./{print $1}' data3
lisi
3.2 匹配操作符

匹配操作符 (~) 使用格式:

数据字段 ~ /正则表达式/

和上面的例子相同,下面第1个例子输出zhangsan的年龄,第2个例子输出年龄在20~30之间的人的名字。


$ gawk 'BEGIN{FS="\n";RS=""}
$1 ~ /zhangsan/{print $1,$2,$3}' data3
zhangsan 17 haerbin

$ gawk 'BEGIN{FS="\n";RS=""}
$2 ~ /^2.$/{print $1,$2,$3}' data3
lisi 20 beijing

3.3 数学表达式

处理正则表达式,gawk还可以在匹配模式中使用数学表达式。
可以使用任何常见的数学表达式:

操作符描述
==等于
<=小于等于
<小于
>大于
>=大于等于

还是相同的例子,输出张三的年龄

$ cat <data3
zhangsan
17
haerbin

lisi
20
beijing

$ gawk 'BEGIN{FS="\n";RS=""}
$1=="zhangsan"{print "age="$2}' data3
age=17

四、结构化命令

gawk 中的结构化命令和 c语言 中的分支和循环结构基本一致。

4.1 if语句

输出年龄是否大于18岁

$ cat <data3
zhangsan
17
haerbin

lisi
20
beijing


$ cat < gawk_cmd
BEGIN {
   FS="\n"
   RS=""
   print "Begin"
}

{
    cmp=""
    age=18
    if ($2 <= age)
    {
        cmp=" less than "
    }
    else
    {
        cmp=" greater than "
    }
    print $1 cmp,age
}

END{
    print "End of file"
}


$ gawk -f gawk_cmd data3
Begin
zhangsan less than  18
lisi greater than  18
End of file

4.2 while 语句

格式:

while (condition)
{
 statements
}

下面例子输出某次跳远比赛各个选手的平均成绩。

$ cat < data1
5.5 6.5 7.5
6.6 6.5 6.5
6.6 7.5 5.9

$ cat < gawk_cmd
BEGIN {
   print "Begin"
}

{
    i=1
    sum=0.0
    while (i <= NF)
    {
        sum+=$i
        i++
    }
    print sum/NF
}

END{
    print "End of file"
}

$ gawk -f gawk_cmd data1
Begin
6.5
6.53333
6.66667
End of file
4.3 do-while语句

格式:

do
{
 statements
}
while (condition)

do-whilewhile 唯一区别是 do-while保证statements会在条件被求值之前至少执行一次。

4.4 for语句

格式:

for (变量赋值; condition; 迭代处理)
{
 statements
}

下面使用for语句计算跳远比赛的平均成绩。

$ cat < gawk_cmd
BEGIN {
   print "Begin"
}

{
    sum=0.0
    for(i=1;i<=NF;i++)
    {
        sum+=$i
    }
   
    print sum/NF
}

END{
    print "End of file"
}

$ gawk -f gawk_cmd data1
Begin
6.5
6.53333
6.66667
End of file
五、格式化打印
5.1 printf 命令

gawk中的printf命令和 C语言 中的printf函数一样。
格式:

printf “format string”,var1,var2…

格式说明符的控制字母

控制字母描述
c数字作为ASCII字符显示
d和i显示整数值
e用科学计数法显示数字
f显示浮点数
g用科学计数法或浮点数显示(较短的格式有限)
o显示八进制
s显示字符串
x显示十六进制
X显示十六进制,但用大写字母A-F
#将输入数据当做字符串
$ echo "200.33333"| gawk '{printf "%s\n",$1}'
200.33333

#将输入数据当作小数并保留两位小数
$ echo "200.33333"| gawk '{printf "%2.2f\n",$1}'
200.33

#将输入数据当作整数,并且至少输出5位,不足5位向前补0
$ echo "200.33333"| gawk '{printf "%05d\n",$1}'
00200
六、自定义函数
6.1 定义函数

格式:

function name ([variables])
{
  statements
}

6.2 使用函数

使用自定义函数计算平均成绩

$ cat < gawk_cmd
function average(s1,s2,s3)
{
    return (s1 + s2 + s3)/3
}

BEGIN {
   print "Begin"
   
}

{
    printf "average scores:%.2f\n",average($1,$2,$3)
}

END{
    print "End of file"
}


$ gawk -f gawk_cmd data1
Begin
average scores:6.50
average scores:6.53
average scores:6.67
End of file
6.3 函数库

也可以把常用的函数放到一个文件中,封装成函数库。

$ cat < lib_func
function average(s1,s2,s3)
{
    return (s1 + s2 + s3)/3
}

$ cat < lib_func
function average(s1,s2,s3)
{
    return (s1 + s2 + s3)/3
}
ubuntu@VM-8-14-ubuntu:~$ cat < gawk_cmd

BEGIN {
   print "Begin"
}

{
    printf "average scores:%.2f\n",average($1,$2,$3)
}

END{
    print "End of file"
}

$ gawk -f lib_func -f gawk_cmd  data1
Begin
average scores:6.50
average scores:6.53
average scores:6.67
End of file

  • 18
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值