shell脚本学习8-gawk

本节gawk是awk的gun版本,gawk实际上是一种编程语言而不是一个命令。gawk要用单引号和大括号包含进来,把程序写到大括号里面。

  1. gawk处理一行数据时的分隔符
    gawk会把一行数据按照任意的空白字符(比如空格或者制表符)进行分割,1代表第一个数据段,以此类推。比如文件data的内容为:
This is Fruit:apple
That is City:Beijing
These are books:maths
Those are animals:dogs

按照默认的空白字符进行分割,输入:

gawk '{print $3}' data

运行后结果为:

Fruit:apple
City:Beijing
books:maths
animals:dogs

如果想要修改分割符,比如以冒号作为分隔符,则输入:

gawk -F: '{ print $2}' data

运行后结果为:

apple
Beijing
maths
dogs
  1. gawk中使用多个指令
    gawk中使用多个指令的时候,除了最后一个指令外,每个指令的结尾都要加一个分号。比如输入
gawk '{$1="This";print $0}' data

运行后结果为:

This is Fruit:apple
This is City:Beijing
This are books:maths
This are animals:dogs
  1. 把指令放进文件中,用gawk去加载文件
    比如建立一个文件gawk_file,内容为:
{
str="-->"
print $1 str $2
}

输入:

gawk -f gawk_file data

运行后输出:

This-->is
That-->is
These-->are
Those-->are

注意上面的gawk_file文件中去取str变量的值的时候并没有用美元符号,而且指令与指令之间也不需要用分号。

  1. BEGIN关键字
    BEGIN关键字后面可以加语句,这些语句会在gawk读取文件之前就执行。比如输入:
gawk 'BEGIN {print "Read file:..."} {print $0}' data

运行结果为:

Read file:...
This is Fruit:apple
That is City:Beijing
These are books:maths
Those are animals:dogs
  1. END关键字
    END关键字后面可以加语句,这些语句会在gawk处理文件后再执行。比如输入:
gawk 'BEGIN {print "Read file:..."} {print $0} END{print "Done!"}' data

运行后结果为:

Read file:...
This is Fruit:apple
That is City:Beijing
These are books:maths
Those are animals:dogs
Done!

可以把BEGIN,END等都写到一个文件中去,比如建立一个文件gawk_file2,内容为:

BEGIN {
print "Start"
print "Begin to read file"
FS=":"
}

{
print $0
}

END {
print "END"
}

输入:

gawk -f gawk_file2 data

运行后输出结果为:

Start
Begin to read file
This is Fruit:apple
That is City:Beijing
These are books:maths
Those are animals:dogs
END
  1. 分隔符内建变量
    内建变量值得是shell中已经定义过可以直接使用的变量,shell中用于分割作用的内建变量有5个,之前已经提到过一个FS,FS是输入字段的分隔符,除了FS还有FIELDWIDTH,OFS,RS,ORS。

6.1 OFS对输出进行分割
默认情况下,对输出进行分割的是空格。当然如果设置OFS的话那么就能修改风格符了,比如输入:

gawk 'BEGIN {FS=":";OFS="<--->"} {print $1,$2}' data

运行后结果为:

This is Fruit<--->apple
That is City<--->Beijing
These are books<--->maths
Those are animals<--->dogs

如上所示,在BEGIN里面把输入分隔符和输出分隔符都设置号。

6.2 FIELDWIDTHS按宽度取出数据
FIELDWIDTHS设定之后,gawk根据输入的数字在输入的一行中去截取对应的长度,同时FIELDWIDTHS设定之后FS就不起作用了。比如对于文本data的数据为:

This is Fruit:apple
That is City:Beijing
These are books:maths
Those are animals:dogs

输入:

gawk 'BEGIN{FIELDWIDTHS="4 1 2 1 5 6";OFS="<--->"} {print $1,$3,$5,$6}' data

运行后结果为:

This<--->is<--->Fruit<--->:apple
That<--->is<--->City:<--->Beijin
Thes<---> a<--->e boo<--->ks:mat
Thos<---> a<--->e ani<--->mals:d

6.3 RS作为输入记录的分隔符
默认情况下,RS为换行符,也就说对于一个文本,每一行就是一个新的记录。之前的比如2这种都是对一个记录去处理的。有时候我们希望多行数据为一个记录的时候,可以修改RS的值。比如有一个文本data2的内容为:

Fan wei
China Beijing
Male
Actor

Ji Xiao Lan
Qing Beijing
Male
Guan

以上内容实际上是两段,现在想要把一段当成一个整体,并且整段中的每一行当做一个数据段。这里只要把FS设置为换行符\n,并把RS设置成空行。输入如下:

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

运行后结果为:

Fan wei China Beijing
Ji Xiao Lan Qing Beijing

6.4 ORS作为输出记录的分隔符
同理,ORS作为输出记录的分隔符,默认也是换行符\n。当然也可以修改这个变量,输入:

gawk 'BEGIN{FS="\n"; RS="";ORS="<>\n"} {print $1,$2}' data3

data3的内容为:

Fan wei
China Beijing
Male
Actor

Ji Xiao Lan
Qing Beijing
Male
Guan

运行后结果为:

Fan wei China Beijing<>
Ji Xiao Lan Qing Beijing<>
  1. 自定义变量
    gawk中也可以自己定义一些变量并使用,在使用变量的时候不需要加美元符号。比如在gawk中定义并打印字符串:
gawk 'BEGIN{str="Wu lin wai zhuan";print str}'

运行后输出结果为:

Wu lin wai zhuan

gawk中也可以使用数学运算,如输入:

gawk 'BEGIN{x=4;x=x*x+4;print x}'

运行后结果为:

20
  1. gawk中使用字典
    gawk中能够定义字典,类似于C++中的map一样,比如输入:
gawk 'BEGIN {city["JiangSu"]="NanJing";city["JiangXi"]="NanChang";print city["JiangSu"]}'

运行后输出结果为:

NanJing

当然,也可以用数字,比如:

gawk 'BEGIN{array[1]=3;array[2]=5;multi=array[1]*array[2];print multi}'

运行后输出结果为:

15

同时,也可以使用迭代语句来读取字典中的值。gawk中使用for语句来取出字典中的数据,不过每次取出的都是字典中key值。比如输入:

gawk 'BEGIN{dict["a"]=1;dict["b"]=2;dict["c"]=3;dict["d"]=4;for (idx in dict){print "index", idx,"value",dict[idx]}}'

运行后结果为:

index a value 1
index b value 2
index c value 3
index d value 4

注意index的返回值没有规律,也就是说多次执行的时候可能index返回的顺序并不相同。同时也可以删除字典中的一个对象,比如输入:

gawk 'BEGIN{dict["a"]=1;dict["b"]=2;dict["c"]=3;dict["d"]=4;delete dict["d"];for (idx in dict){print "index", idx,"value",dict[idx]}}'

运行后结果为:

index a value 1
index b value 2
index c value 3
  1. gawk中使用正则表达式
    gawk中可以使用正则表达式去匹配,当然详细的正则表达式的内容这一节不展开。这里以文件data4为例,介绍如何使用正则表达式,data4的内容为:
Vedio wulin is awesome
Apparently that is correct
which vulin

首先不用正则表达式,搜索匹配wulin这个单词并把第一个字段打印出来,输入:

gawk '/wulin/{print $1}' data4

运行后结果为:

Vedio

这是因为data4中的三行数据只有 第一行有wulin这个单词。现在使用正则表达式,输入:

gawk '/[uvw]ulin/{print $1}' data4

运行后结果为:

Vedio
which

因为[uvw]这个表示属于这三个字母的任何一个都匹配,所以第一行和第三行的wulin和vulin都匹配上了。

上面的正则表达式是使用文本的一整行去匹配,但是如果想要用一行中的某一个字段去匹配的话也可以的,这个时候要使用匹配操作符(波浪号~)。输入:

gawk 'BEGIN{FS=":"} $1 ~ /^san/{print $1 $NF}' /etc/passw

运行后结果为:

saned /bin/false

其对应的原始数据为:

saned:x:119:127::/var/lib/saned:/bin/false

这个命令会在第一个字段中去判断$1是不是以san开头的单词,如果是的话则打印第一个字段和最后一个字段。

如果想要排除这个正则表达式的关键字的时候则可以使用!,也就是说用!可以找出不匹配的行然后去处理,比如输入

gawk 'BEGIN{FS=":"} $1 !~ /^san/{print $1, $NF}' /etc/passwd|grep san

运行后结果中已经没有之前的saned的行了,但是/etc/passwd的其他行的数据都有。

  1. gawk中使用数字和字符串的比较
    对于数字的比较跟C语言的语法相同,有以下5种:
    a)数字x与数字y相等 ,x==y
    b)数字x大于数字y, x>y
    c)数字x大于等于数字y, x>=y
    d)数字x小于数字y, x e)数字x小于等于数字y, x<=y
    新建文件data5,内容为:
100 madashuai xiaozhang
200 fandebiao chuzi
300 yufugui   cunzhang

输入:

gawk '$1==200 {print $0}' data5

运行后结果为:

200 fandebiao chuzi

再输入:

gawk '$1<=200 {print $0}' data5

运行后结果为:

100 madashuai xiaozhang
200 fandebiao chuzi

上面两个输入都是数字的比较,字符串同样也可以比较,当字符串完全相同的时候才能匹配,输入:

gawk '$2=="fandebiao" {print $0}' data5

运行后结果为:

200 fandebiao chuzi

再输入:

gawk '$2=="fande" {print $0}' data5

运行后结果为空,这说明必须要完全匹配才能处理。

  1. gawk中使用分支语句
    gawk中可以使用if-else这样的分支语句,其语法与C语言的语法相同,比如data5的内容为:
100 madashuai xiaozhang
200 fandebiao chuzi
300 yufugui   cunzhang

输入:

gawk '{if ($1 ==300){x=$1;x=x*x;print x}}' data5

运行后结果为:

90000

当然,gawk中也可以使用else if和else语句,同样和C语言的语法相同,输入:

gawk '{if ($1==100) {print $1}else if($1 ==200 ){print $2} else {print $3}}' data5

运行后结果为:

100
fandebiao
cunzhang
  1. gawk中使用while语句
    gawk中可以使用while循环,语法与C语言相同,比如data6的内容如下:
1 2 3 4
5 6 7 8
9 10 -3 -99

输入:

gawk '{sum=0;i=1;while (i<=4){sum+=$i;i++;}avg=sum/3;print "average:",avg}' data6

运行后结果为:

average: 3.33333
average: 8.66667
average: -27.6667

上面的命令是把每一行的四个数字求和除以3然后打印出来。同时,gawk的while循环中也可以使用break和continue,输入

gawk '{sum=0;i=1;while (i<=4){sum+=$i;if (i==2){break}i++;}avg=sum/3;print "average:",avg}' data6

运行后结果为:

average: 1
average: 3.66667
average: 6.33333
  1. gawk中使用for语句
    gawk中使用for语句跟C语言相同,输入的data6文件内容为:
1 2 3 4
5 6 7 8
9 10 -3 -99

输入如下命令:

gawk '{sum=0;for (i=1;i<=4;i++){sum+=$i;}avg=sum/3;print "average:",avg}' data6 

运行后结果为:

average: 3.33333
average: 8.66667
average: -27.6667
  1. gawk中使用printf打印
    gawk中除了可以使用print语句外,还可以使用printf语句打印,用法和C语言相同。比如data7文本的内容为:
312 3.145 great
666 45.01 good
899 0.004 nothing

输入:

gawk '{printf "integer:%d,   float:%f,   string:%s\n",$1,$2,$3}' data7

运行后结果为:

integer:312,   float:3.145000,   string:great
integer:666,   float:45.010000,   string:good
integer:899,   float:0.004000,   string:nothing
  1. gawk中使用内建函数
    gawk中有一些内建函数用于处理比较通用的运算,比如数学运算和字符串处理。
    14.1 gawk中使用数学运算的内建函数
    常用的数学运算函数有sin,cos,sqrt,int等等,int是用于去整数值的运算,输入:
gawk 'BEGIN {y=int(3.14);z=int(-3.14);print y,z}'

运行后结果为:

3 -3

14.2 gawk中使用字符串处理的函数
个人感觉这一节非常重要,之后可以用到的地方非常多。

14.2.1 toupper,tolower,length的使用
输入文本data8的内容为:

WuLinWaiZhuan,ZhaoBenShan

输入:

 gawk 'BEGIN{FS=","}{x=$1;y=toupper(x);z=tolower(x);printf("%s,   %s,   %s,   %d\n"),x,y,z,length(z)}' data8

运行后结果为:

WuLinWaiZhuan,   WULINWAIZHUAN,   wulinwaizhuan,   13

toupper和tolower函数将字符串分别转化成大写和小写,length返回字符串的长度。

14.2.2 split函数分割字符串
split是按照指定的分割字符去分割字符串,返回值是分割后的字符串的个数,输入:

echo "12:34:56:78"|gawk '{split($0,a,":");for(i=1;i<=4;i++){print a[i]}}'

运行后结果为:

12
34
56
78

当在shell脚本中去对字符串引用时候,要使用"和"组成的双引号。建立一个shell脚本,输入:

#/bin/bash
str=12:34:56:78
awk 'BEGIN {num=split('"\"$str\""', array, ":");for (i=1;i<=num;i++){print array[i]}}'

运行这个脚本后,结果为:

12
34
56
78

14.2.3 gawk中使用index函数
index(s,t)是查找字符串t在s字符串索引值,如果找不到返回0,index 返回子字符串第一次被匹配的位置,偏移量从位置1开始.输入:

awk 'BEGIN { a="mytest";b="test";print index(a, b) }'

运行后结果为:

3

再输入:

awk 'BEGIN { a="mytest";b="ttt";print index(a, b) }'

运行后结果为:

0

14.2.4 gawk中使用sub和gsub函数
sub和gsub都是用于字符串替换,sub只会替换一行中第一次的成功匹配,gsub会替换所有的成功匹配,比如文件data8的内容为:

LinWaiZhuan,ZhaoBenShanLin

输入:

gawk 'BEGIN{FS=","}{sub("Lin","Test");print $0}' data8

运行后结果为:

TestWaiZhuan,ZhaoBenShanLin

sub函数只修改了第一个Lin为Test。
再输入:

gawk 'BEGIN{FS=","}{gsub("Lin","Test");print $0}' data8

运行后结果为:

TestWaiZhuan,ZhaoBenShanTest

gsub把所有的Lin都改成Test
注意:sub和gsub可以使用正则表达式,比如输入:

gawk 'BEGIN{FS=","}{gsub("^Lin","Test");print $0}' data8

运行后结果为:

TestWaiZhuan,ZhaoBenShanLin

这里用逗号把一行数据分割之后,修改以Lin开头的字段。

14.2.5 substr函数截取字符串
substr(str, start,len)从字符串str的start索引位置截取len长度的字符串,如果么有指定len这个参数的话,那就截取到结尾。
输入:

awk 'BEGIN { print substr( "hello world", 7,11 ) }'

运行后结果为:

world

再输入:

awk 'BEGIN { print substr( "hello world", 4 ) }'

运行后结果为:

lo world

14.2.6 match函数正则表达式匹配
match 返回在字符串中正则表达式位置的索引,如果找不到指定的正则表达式则返回0。match函数会设置内建变量RSTART为字符串中子字符串的开始位 置,RLENGTH为到子字符串末尾的字符个数。substr可利于这些变量来截取字符串
输入:

awk 'BEGIN{start=match("this is a test",/[a-z]+$/); print start}'

运行后结果为:

11

再输入:

awk 'BEGIN{start=match("this is a test",/[a-z]+$/); print start, RSTART, RLENGTH }'

运行后结果为:

11 11 4
```gawk中使用数字和字符串的比较

15. 总结与展望
15.1 总结
a) gawk中对一行数据进行分割的时候指定FS的值;
b) gawk中使用多个指令的时候用分号分割;
c) 把指令写到文件中,用-f去加载文件;
d) BEGIN和END关键字使用;
e) 分隔符内建变量的使用,FS,FIELDWIDTH,OFS,RS,ORS;
f) gawk中使用自定义变量不需要用美元符号;
g) gawk中使用字典;
h) gawk中使用正则表达式,!~不包含;
i)  gawk中使用数字和字符串的比较;
j)gawk中使用分支语句和循环语句(while,for);
k) gawk中使用printf打印
l)   gawk中使用内建函数
15.2 展望
下一节学习shell脚本中的正则表达式,非常重要。


你可能感兴趣的:(shell脚本学习8-gawk)