十八、gawk进阶

gawk支持两种变量

  1. 内建变量
  2. 自定义变量

 

内建变量

用来存放处理数据文件中的数据字段。

 

如数据字段变量

第一个数据字段变量$1表示,第二个用$2依次类推。

 

字段分隔符

模式空格或制表符

使用-F

 

常见的内建变量

变量 描述
FIELDWIDTHS 由空格分隔的一列数字,定义每个数据字段确定宽度
FS 输入字段分隔符
RS 输入记录分隔符
OFS 输出字段分隔符
ORS 输出记录分隔符
ARGC 当前命令行参数个数
ENVIRON 当前shell环境变量及其值组成的关联数组
NF 数据文件中字段总数
FNR 当前数据文件中的数据行数
NR 已处理的输入记录数

 

OFS默认为空格,跟FS功能一样,但是是用在print命令输出上。

 

样本文件data1.txt

[root@tzPC 22Unit]# cat data1
data11,data12,data13,data14,data15
data21,data22,data23,data24,data25
data31,data32,data33,data34,data35

FS定义以逗号分隔字符

[root@tzPC 22Unit]# gawk 'BEGIN{FS=","}{print $1,$2,$3}' data1
data11 data12 data13
data21 data22 data23
data31 data32 data33

OFS变量能将定义输出分隔字符

[root@tzPC 22Unit]# gawk 'BEGIN{FS=",";OFS="-"}{print $1,$2,$3}' data1
data11-data12-data13
data21-data22-data23
data31-data32-data33

 

FIELDWIDTHS变量可以不靠字段分隔符来读取记录,而使用字段宽度来计算字段。

一旦设定FIELDWIDTHS变量就不能改了,不适用变长的字段。

样本文件data1b

[root@tzPC 22Unit]# cat data1b
1005.3247596.37
115-2.349194.00
05810.1298100.1

可以看到FIELDWIDTHS定义了分别以3个字符,5个字符,2个字符,5个字段长度分隔

[root@tzPC 22Unit]# gawk 'BEGIN{FIELDWIDTHS="3 5 2 5"}{print $1,$2,$3,$4}' data1b
100 5.324 75 96.37
115 -2.34 91 94.00
058 10.12 98 100.1

 

RS跟ORS默认为换行符

样本文件data2

[root@tzPC 22Unit]# cat data2
Riley Mullen
123 Main Street
Chicago, IL  60601
(312)555-1234

Frank Williams
456 Oak Street
Indianapolis, IN  46201
(317)555-9876

Haley Snell
4321 Elm Street
Detroit, MI  48201
(313)555-4938

FS变量设置为换行符,表示每行都是一个单独的字段

RS设置为空字符串,表示每个空白行当作一个记录分隔符

输出每条记录的第一个字段跟第四个字段,注意以空行分隔为一条记录,即1234行为一条记录,以\n为分隔字符,即1行为一个字段,输出14字段即$1跟$4

[root@tzPC 22Unit]# gawk 'BEGIN{FS="\n"; RS=""}{print $1,$4}' data2
Riley Mullen (312)555-1234
Frank Williams (317)555-9876
Haley Snell (313)555-4938

 

ARGC和ARGV变量

这两个变量可以获取命令行参数的总数以及他们的值。

[root@tzPC 22Unit]# gawk 'BEGIN{print ARGC,ARGV[1]}' data1
2 data1

ARGC表明命令行上有2个参数,分别是gawk跟data1这两个

ARGV数组是从索引0开始,0代表命令gawk,1代表参数data1,中间的脚本不算参数

[root@tzPC 22Unit]# gawk 'BEGIN{print ARGC,ARGV[0]}' data1
2 gawk
[root@tzPC 22Unit]# gawk 'BEGIN{print ARGC,ARGV[1]}' data1
2 data1
[root@tzPC 22Unit]# gawk 'BEGIN{print ARGC,ARGV[2]}' data1
2 
[root@tzPC 22Unit]# 

 

ENVIRON变量

使用关联数组来提取shell环境变量

关联数组以文本如环境变量名HOME作为索引值

[root@tzPC 22Unit]# gawk '
> BEGIN{
> print ENVIRON["HOME"]
> print ENVIRON["PATH"]
> }'
/root
/mnt/mysql/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@tzPC 22Unit]# 

 

NF变量

样本文件/etc/passwd

[root@tzPC 22Unit]# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

FS变量设置以:分隔,OFS变量设置以:输出,NF变量为字段总数,$NF变量为最后一个数据字段的值

[root@tzPC 22Unit]# gawk 'BEGIN{FS=":"; OFS=":"}{print $1,NF}' /etc/passwd
root:7
bin:7
daemon:7
adm:7
lp:7

[root@tzPC 22Unit]# gawk 'BEGIN{FS=":"; OFS=":"}{print $1,$NF}' /etc/passwd
root:/bin/bash
bin:/sbin/nologin
daemon:/sbin/nologin
adm:/sbin/nologin
lp:/sbin/nologin

 

FNR变量

样本文件data1

[root@tzPC 22Unit]# cat data1
data11,data12,data13,data14,data15
data21,data22,data23,data24,data25
data31,data32,data33,data34,data35
[root@tzPC 22Unit]# 

gawk命令行定义了两个输入文件data1,脚本会打印第一个数据字段的值和FNR变量的当前值,gawk程序处理第二个数据文件时,FNR值归1

[root@tzPC 22Unit]# gawk 'BEGIN{FS=","}{print $1,"FNR="FNR}' data1 data1
data11 FNR=1
data21 FNR=2
data31 FNR=3
data11 FNR=1
data21 FNR=2
data31 FNR=3

 

NR变量

还是data1样本文件

gawk程序在处理第二个数据文件时,NR值继续没有归1而是继续计数

[root@tzPC 22Unit]# gawk 'BEGIN{FS=","}{print $1,"FNR="FNR,"NR="NR}' data1 data1
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

 

自定义变量

变量命名规则

任意长度的字母、数字、下划线

不能以数字开头

区分大小写

 

在脚本中给变量赋值

[root@tzPC 22Unit]# gawk '
> BEGIN{
> testing="This is a test"
> print testing
> testing=45
> print testing
> }'
This is a test
45

使用算数运算

[root@tzPC 22Unit]# awk 'BEGIN{x=4; x=x*2+3; print x}'
11

 

在命令行上给变量赋值

输出data1每行第三个字段

[root@tzPC 22Unit]# cat script1 
BEGIN{FS=","}
{print $n}
[root@tzPC 22Unit]# gawk -f script1 n=3 data1
data13
data23
data33

要让变量在BEGIN中使用需要在gawk后接-v选项,注意-v必须放在脚本代码前

[root@tzPC 22Unit]# cat script2
BEGIN{print "The starting value is " n; FS=","}
{print $n}
[root@tzPC 22Unit]# gawk -v n=3 -f script2 data1
The starting value is 3
data13
data23
data33

 

定义数组

 gawk编程语言使用关联数组,即使用字符串作为数组的索引值。

语法

var[index] = element

var是变量名,index是索引值,element数组元素值

[root@tzPC 22Unit]# gawk 'BEGIN{
> capital["Illinois"] = "Springfield"
> print capital["Illinois"]
> }'
Springfield

算数运算

[root@tzPC 22Unit]# gawk 'BEGIN{
> var[1] = 34
> var[2] = 3
> total = var[1] + var[2]
> print total
> }'
37

 

遍历数组

使用for循环

这里需要注意的是,test存储的是索引值,var是变量名

索引值输出是无序的,以agmn定义,并不会以agmn顺序输出

[root@tzPC 22Unit]# gawk 'BEGIN{
> var["a"] = 1
> var["g"] = 2
> var["m"] = 3
> var["n"] = 4
> for (test in var)
> {
>   print "Index:",test,"  Value:",var[test]
> }
> }'
Index: m   Value: 3
Index: n   Value: 4
Index: a   Value: 1
Index: g   Value: 2

 

删除数组变量

这里有个有趣的地方,这里for 可以不写do-done跟{},写在一行跟换行写都能执行成功

[root@tzPC 22Unit]# gawk 'BEGIN{
var["a"] = 1
var["g"] = 2
for (test in var)
{
  print "Index:",test,"  Value:",var[test]
}
delete var["g"]
print "数组键值g已删除"
for (test in var)
  print "Index:",test,"  Value:",var[test]
}'
Index: a Value: 1 Index: g Value: 2 数组键值g已删除 Index: a Value: 1

 

正则表达式

正则表达式必须出现在它要控制的程序脚本前

样本文件

[root@tzPC 22Unit]# cat data1
data11,data12,data13,data14,data15
data21,data22,data23,data24,data25
data31,data32,data33,data34,data35
[root@tzPC 22Unit]# 

如/11/正则表达式写在脚本前

[root@tzPC 22Unit]# gawk 'BEGIN{FS=","} /11/{print $1}' data1
data11

 

匹配操作符~

 匹配操作符可以将正则表达式使用在特定的数据字段上。

$1表示第一个数据字段,此表达式会匹配第一个字段是以data开头的所有记录

$1 ~ /^data/

该脚本会匹配第二个字段是以data2开头的并打印出此行

[root@tzPC 22Unit]# gawk 'BEGIN{FS=","} $2 ~ /^data2/{print $0}' data1
data21,data22,data23,data24,data25

以:作为分隔符,第一个字段是以root开头的行,输出第一个字段跟最后一个字段

[root@tzPC 22Unit]# gawk -F: '$1 ~ /root/{print $1,$NF}' /etc/passwd
root /bin/bash

可以使用!排除正则表达式的匹配

[root@tzPC 22Unit]# gawk -F: '$1 !~ /root/{print $1,$NF}' /etc/passwd
bin /sbin/nologin
daemon /sbin/nologin
adm /sbin/nologin
lp /sbin/nologin
...

 

使用数学表达式

显示第四个字段即组ID为0的用户

[root@tzPC 22Unit]# gawk -F: '$4 == 0{print $1}' /etc/passwd
root
sync
shutdown
halt
operator

还能使用比较表达式

==、<=、<、>=、>等

[root@tzPC 22Unit]# gawk -F, '$1 == "data11" {print $1}' data1
data11

 

使用结构化命令if

 语法格式

if (condition)
    statement1
#或者
if (condition) statement1

样本文件data4

[root@tzPC 22Unit]# cat data4
23
123
4
45
56
7
23

找出大于20的第一个字段

[root@tzPC 22Unit]# gawk '{if ($1 > 20) print $1}' data4
23
123
45
56
23

如果要在if语句中使用多条命令需要使用{}

[root@tzPC 22Unit]# gawk '{
> if ($1 >20)
> {
>   x= $1 * 2
>   print x
> }
> }' data4
46
246
90
112
46

if-else语句

[root@tzPC 22Unit]# gawk '{
> if ($1 > 20)
> {
>   x= $1 * 2
>   print x
> } else
> {
>   x= $1 /2
>   print x
> }}' data4
46
246
2
90
112
3.5
46

如果单行写,if语句部分需要加分号

[root@tzPC 22Unit]# gawk '{if ($1 > 20) print $1 * 2; else print $1 / 2 }' data4
46
246
2
90
112
3.5
46

 

while语句

样本文件data5

[root@tzPC 22Unit]# cat data5
155 158 121
121 154 165
188 164 125

计算每行平均值

[root@tzPC 22Unit]# gawk '{
> total = 0
> i = 1
> while (i < 4)
> {
>   total += $i
>   i++
> }
> avg = total /3
> print "Average:",avg
> }' data5
Average: 144.667
Average: 146.667
Average: 159

可以使用break跟continue语句

计算每行前两个数的平均值

[root@tzPC 22Unit]# gawk '{
> total = 0
> i = 1
> while (i < 4)
> {
>   total += $i
>   if (i == 2)
>     break
>     i++
> }
> avg = total /2
> print "The average of the first two data elements is: ",avg
> }' data5
The average of the first two data elements is:  156.5
The average of the first two data elements is:  137.5
The average of the first two data elements is:  176

average平均,elements元素

 

do-while语句

类似while语句,但会在检查条件之前执行命令。

该语句保证在检查条件之前至少先执行一次命令。

读取样本文件data5中每行的每个字段并将它累加到一起,直到>=150后输出

[root@tzPC 22Unit]# gawk '{
> total = 0
> i = 1
> do
> {
>   total += $i
>   i++
> } while (total <150)
> print total }' data5
155
275
188

 

for语句

gawk编程语言支持C风格的for循环

 同样计算data5每行平均值

[root@tzPC 22Unit]# gawk '{
> total = 0
> for (i = 1; i < 4; i++)
> {
>   total += $i
> }
> avg = total /3
> print "Average:",avg
> }' data5
Average: 144.667
Average: 146.667
Average: 159

 

格式化打印printf命令

语法格式

print "format string", var1, var2 ...

format string格式化指定符,指定变量如何显示。

第一个format string对应第一个变量,依次类推

 

格式化指定符控制字母

控制字母 描述 控制字母 描述
c 将一个数作为ASCII字符显示 g 用科学计数法或浮点数显示
d 显示一个整数 o 显示一个八进制数
i 显示一个整数 s 显示一个文本字符串
e 用科学计数法显示一个数 x 显示一个十六进制数
f 显示一个浮点数 X 显示一个十六进制,但用大写字母A~F

注意使用控制字母前需要加上%

用科学计数法显示一个很大的数值

[root@tzPC 22Unit]# gawk 'BEGIN{
> x = 10 * 100
> printf "The answer is: %e\n",x
> }'
The answer is: 1.000000e+03

 

出了控制字母,还有3种修饰符

  • width:只当输出字段长度,如果小于这个值,printf会将文本右对齐并以空格填充,如果大于这个值,则按实际长度输出。
  • prec:指定小数点后几位
  • -:采用左对齐

样本文件data2

[root@tzPC 22Unit]# cat data2
Riley Mullen
123 Main Street
Chicago, IL  60601
(312)555-1234

Frank Williams
456 Oak Street
Indianapolis, IN  46201
(317)555-9876

Haley Snell
4321 Elm Street
Detroit, MI  48201
(313)555-4938

使用print输出

[root@tzPC 22Unit]# gawk 'BEGIN{FS="\n"; RS=""}{print $1,$4}' data2
Riley Mullen (312)555-1234
Frank Williams (317)555-9876
Haley Snell (313)555-4938

使用printf输出

[root@tzPC 22Unit]# gawk 'BEGIN{FS="\n"; RS=""}{printf "%s %s\n", $1, $4}' data2
Riley Mullen (312)555-1234
Frank Williams (317)555-9876
Haley Snell (313)555-4938

你会发现没有差别,是不是很惊喜!哈哈!

 

指定每行第一个字段输出宽度为16个字符,默认是右对齐

[root@tzPC 22Unit]# gawk 'BEGIN{FS="\n"; RS=""}{printf "%16s %s\n", $1, $4}' data2
    Riley Mullen (312)555-1234
  Frank Williams (317)555-9876
     Haley Snell (313)555-4938

加-变成左对齐

[root@tzPC 22Unit]# gawk 'BEGIN{FS="\n"; RS=""}{printf "%-16s %s\n", $1, $4}' data2
Riley Mullen     (312)555-1234
Frank Williams   (317)555-9876
Haley Snell      (313)555-4938

 

使用%15.2f格式指定printf输出将浮点值近似到小数点后两位,15是宽度值

[root@tzPC 22Unit]# gawk '{
total = 0
for (i = 1; i < 4; i++)
{
  total += $i
}
avg = total /3
printf "Average: %15.2f\n",avg
}' data5
Average:          144.67
Average:          146.67
Average:          159.00

 

内建函数

 gawk编程语言提供了不少内置函数,加快编码效率。

 

数学函数

gawk数学函数

函数 描述 函数 描述
atan2(x,y) x/y的反正切 rand() 取大于0小于1的随机浮点值
cos(x) x的余弦 sin(x) x的正弦
int(x) x的整数部分 sqrt(x) x的平方根
log(x) x的自然对数 srand(x) 为计算随机数指定一个初始值

看到这几个算数符有没有回到初中的感觉哈哈。

 int()相当于转整型,值为5.6时取5,不会四舍五入

rand()比较常用,取随机数。

返回0~9的随机数

x = int(10 * rand())

 

gawk语言对处理的数值有确定范围,如果数值过大会报错

[root@tzPC 22Unit]# gawk 'BEGIN{x=exp(1000);print x}'
gawk: cmd. line:1: warning: exp: argument 1000 is out of range
inf
[root@tzPC 22Unit]#

 

gawk还支持按位操作数据的函数,对处理二进制值很有用,我目前暂时不需要,在书P488

 

字符串函数

 书P488有个表,太多了,写几个常用的就行了

小写转大写

toupper(s)

[root@tzPC 22Unit]# gawk 'BEGIN{x = "testing"; print toupper(x);print length(x)}'
TESTING
7

 

asort(s,d)

将数组s按数据元素值排序,排序后的数组会储存在数组d中

[root@tzPC 22Unit]# gawk 'BEGIN{
> var["a"] = 1
> var["b"] = 2
> var["c"] = 3
> var["d"] = 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

可以看到,test的索引值跟数据元素都已经变成数组var排序后的数据元素值了,且asort是按照ascii码排序的。

 

asorti(s,d)

将数组s按索引排序,排序后的数组会储存在数组d中

 

 split(s, a [r])

将s使用FS字符或正则表达式r(可选)处理后分开放到数组a中

将data1中的每行使用逗号分割,放入数组var中,打印索引1跟5中的元素

[root@tzPC 22Unit]# 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格式指定的日期转换成时间戳
strftime(format timestamp)  将当前时间戳或timestamp(可选)转换成格式化日期
systime() 返回当前时间戳
[root@tzPC 22Unit]# gawk 'BEGIN{
> date = systime()
> day = strftime("%A, %B %D, %Y", date)
> print day
> }'
Saturday, September 09/19/20, 2020
[root@tzPC 22Unit]# gawk 'BEGIN{
date = systime()
day = strftime("%A, %B %d, %Y", date)
print day
}'
Saturday, September 19, 2020

 

自定义函数

语法格式

function name(variables)
{
    statements
   return value #返回值 }

将返回值赋值给变量

x = myrand(100)

函数必须定义在所有代码块之前(包括BEGIN代码块)

[root@tzPC 22Unit]# gawk '
function myprint()
{
  printf "%-16s  %s\n", $1, $4
}
BEGIN{FS="\n"; RS=""}
{
  myprint()
}' data2
Riley Mullen      (312)555-1234
Frank Williams    (317)555-9876
Haley Snell       (313)555-4938

 

使用函数库

 总所周知,函数库相当于类。

先创建gawk函数文件,也就是相当于java类

[root@tzPC 22Unit]# cat funclib
function myprint()
{
  printf "%-16s  %s\n", $1, $4
}
function printthird()
{
  print $3
}
function myrand(limit)
{
  return int(limit * rand())
}

创建脚本文件

[root@tzPC 22Unit]# cat script4
BEGIN{ FS="\n"; RS=""}
{
  myprint()
}

在命令行上使用-f指定函数库文件以及脚本文件

[root@tzPC 22Unit]# gawk -f funclib -f script4 data2
Riley Mullen      (312)555-1234
Frank Williams    (317)555-9876
Haley Snell       (313)555-4938

 

学习来自:《Linux命令行与Shell脚本大全 第3版》第22章

posted @ 2020-09-04 09:00  努力吧阿团  阅读(213)  评论(0编辑  收藏  举报