【shell】shell学习之awk进行文本处理

简介

       awk在Linux下对文本和数据进行处理,它可以处理数据流、文本文件等,也支持关联数组、递归函数、条件语句等功能。

        awk脚本通常由3部分组成:BEGIN、END和带模式匹配选项的公共语句块(common statement block)。这3部分是可选的,可以不用出现在脚本中。

工作原理

实例分析

实例1:统计文件的行数。

实例2:①awk命令中的print用于打印读取到的内容或经过代码处理之后的内容。当print命令不带参数时,会打印当前行的内容。

           ②当print打印多个参数时,这些参数在print命令中以逗号分隔,终端上显示出来的参数内容则是以空格作为分隔符的。

实例3:如何将外部定义的变量值传递给awk,可以借助选项 -v  。

实例4:awk中包含一些特殊变量

实例5:为字段设置分隔符,默认的字段分割符是空格,可以使用-F选项指定不同的分隔符。

实例6:awk每读取一行,就会检查该行是否匹配指定的模式,模式本身可以是正则表达式、条件语句以及行范围等。

 实例7:使用getline读取行。

 实例9:除了数字和字符串类型的变量,awk还支持关联数组。

工作原理

        awk脚本的结构如下:

$ awk 'BEGIN{ statements } pattern { commands } END{ statements }' file

        awk以逐行的形式处理文件。BEGIN之后大括号{}里的内容会先于公共语句块执行,对于匹配上“pattern”的行,会执行”{ commands }“里面的命令。最后处理完整个文件之后,awk会执行END之后大括号{}里的命令。

        1)BEGIN语句块在awk开始从输入流中读取行之前被执行,是一个可选语句块,负责变量的初始化、打印输出表格的表头等。

        2)执行完BEGIN中的命令,接着从文件或stdin中读取一行(逐行读取,就像一个用来读取行的while循环),如果能够匹配pattern,则执行后面的commands语句块,重复这个过程,直到文件全部被读取完毕。awk对于读取到的每一行,先检查该行是否匹配指定的模式,如果匹配,则会执行该语句块。

        3)当读到输入流末尾时,执行END{ commands }语句块。这一部分是在awk读取完输入流中的所有行之后被执行,项打印所有行的分析结果这种常见任务都是在END语句块中实现的。

实例分析

实例1:统计文件的行数。
$ awk 'BEGIN { i=0 } { i++ } END { print i}' hello_world.txt
#awk的命令可以放在单引号中,也可以放在双引号中。上面的命令还可以这样写:
#awk "BEGIN { i=0 } { i++ } END { print i}" hello_world.txt

 命令解析:在读取文件内容之前,先执行BEGIN语句块中的i=0,也就是初始化变量i,接着逐行读取hello_world.txt中的内容,读取一行,执行一次i++,最后读取完文件内容,执行END语句块,也就是输出最终的总行数。

注:在使用print输出变量时,命令是print i,而不是print $i。

结果:

7

实例2:①awk命令中的print用于打印读取到的内容或经过代码处理之后的内容。当print命令不带参数时,会打印当前行的内容。
$ echo -e "line1\nline2" | awk 'BEGIN { print "Start" } { print } END { print "End" }'

命令解析:读取输入流内容之前,先执行BEGIN中的内容,也就是输出 Start,接着读取输入流第一行"line1",执行不带参数的print,会直接打印读取的内容,然后读取第二行,打印,读取完后执行END中的语句,输出End。

结果:

Start
line1
line2
End

②当print打印多个参数时,这些参数在print命令中以逗号分隔,终端上显示出来的参数内容则是以空格作为分隔符的。
$ echo | awk '{ var1="v1"; var2="v2"; var3="v3"; print var1,var2,var3 }'

 命令解析:echo传入一个空行,awk命令被执行一次。在awk中完成变量的赋值操作后,输出三个变量的值(以逗号分隔三个变量)。

结果:

v1 v2 v3   #可以看出输出变量的结果是以空格作为分隔符的

在awk的print中,双引号被当作拼接操作符,例如:

$ echo | awk '{ var1="v1"; var2="v2"; var3="v3"; print var1"-"var2"-"var3 }'

结果:

 v1-v2-v3

实例3:如何将外部定义的变量值传递给awk,可以借助选项 -v  。
$ var=1000
$ echo | awk -v variable=$var '{ print variable }'

命令解析:在awk外部定义了变量var,值设为1000(注意,shell定义变量时等号两侧不能有空格),awk使用-v选项读取外部变量var,并将其值赋给便变量variable,然后输出variable的值。

结果:

1000

        也可以将外部变量以键-值对的形式给出,当有多个外部变量时,使用空格分隔,作为awk的命令行参数跟在BEGIN、{ }和END语句块之后。具体形式如下: 

$ var1="Variable1"; var2="Variable2"
$ echo | awk '{ print v1,v2 }' v1=$var1 v2=$var2  

结果:

Variable1 Variable2 

实例4:awk中包含一些特殊变量
awk中的特殊变量
变量含义
NR表示记录编号,当awk读取文本行时,该变量相当于行数
NF表示字段数量,在处理当前记录时,确定好分隔符(默认为空格)后,分隔的每一段称为字段。
$0表示当前记录的文本内容
$1表示第一个字段的文本内容
$2表示第二个字段的文本内容

 注:先确定当前记录的分隔符,然后使用$1 $2 $3...获取字段内容。

$ echo -e "line1 f2 f3\nline2 f4 f5\nline3 f6 f7" | awk '{ print "Line No:"NR,",No of fields:"NF, "$0="$0, "$1="$1, "$2="$2, "$3="$3 }'

命令解析:①echo返回的字符串为:

line1 f2 f3
line2 f4 f5
line3 f6 f7

 ②这一段文本的行数是3,也就是有3条记录,每一条记录中有3个字段,由空格分隔,读取第一条记录时,行号(NR)为1,$0输出该条记录的内容,$1、$2和$3分别输出本条记录的三个字段的内容,第二行和第三行行号(NR)分别为2和3。

注:print $NF打印的是最后一个字段的内容。

还可以这样统计文件的行数

$ awk 'END { print NR }' file_name
实例5:为字段设置分隔符,默认的字段分割符是空格,可以使用-F选项指定不同的分隔符。
$ awk -F: '{ print $NF }' file_name
#以冒号:作为分隔符,打印每一条记录最后一个字段的内容

也可以将分隔符的设置放在BEGIN语句块中,

$ awk 'BEGIN { FS=":" } { print $NF }' file_name
#BEGIN语句块中,FS=“分隔符”进行设置
实例6:awk每读取一行,就会检查该行是否匹配指定的模式,模式本身可以是正则表达式、条件语句以及行范围等。

①使用正则表达式进行模式匹配。

/string/ 表示纯字符匹配       !/string/ 表示纯字符不匹配

~/string/ 表示字段值匹配     !~/string/ 表示字段值不匹配

~/a1|a2/字段值匹配a1或a2

$ echo -e "line1 f2 f3\nline2 f4 f5\nline3 f6 f7" | awk '/line1/ { print $0 }'

命令解析:该命令是读取一行文本(一条记录),检查是否包含字符串"line1",如果包含则打印该条记录。

结果:

line1 f2 f3

$ echo -e "line1 f2 f3\nline2 f4 f5\nline3 f6 f7" | awk '$2~/f4/ { print $0 }'

命令解析:该命令是读取一行文本(一条记录),检查第二个字段(分隔符为空格,如果是其他分隔符需要指定)是否包含字符串“4”,如果包含则打印该条记录。

结果:

line2 f4 f5

$ echo -e "line1 f2 f3\nline2 f4 f5\nline3 f6 f7" | awk '$2~/f4|f6/ { print $0 }'

命令解析:该命令是读取一行文本(一条记录),检查第二个字段(分隔符为空格,如果是其他分隔符需要指定)是否包含字符串“f4”或“f6”,如果包含则打印该条记录。

②添加一些限制条件。

awk 'NR < 5'       #行号小于5的行

awk 'NR == 1,NR == 4'       #行号在1-5之间(包含1但不包含5)

$ awk 'NR < 5' hello_world.txt  #打印出hello-world.txt中的前4行
$ awk 'NR == 1,NR == 4' hello_world.txt  #打印出hello-world.txt中的前4行
 实例7:使用getline读取行

        awk默认读取文件中所有行,如果只想读取某一行,可以使用getline函数。它可以在BEGIN语句块中读取文件的头部信息,然后在主语句块中处理余下的实际数据。

语法为:

getline var #var表示特定行,也可以不带参数,则表示获取当前行的下一行

例如: 

$ seq 5 | awk 'BEGIN { getline; print "read the line is:"$0 } { print $0}'

结果:

read the line is:1
2
3
4
5

从结果看出,BEGIN中使用getline,获取了第一行的记录,其余的记录都逐行传入后面的语句块。 

        再来看看getline在BEGIN语句块和主语句块中的获取特定行的情况。

$ seq 5 | awk 'BEGIN { getline; print "read the line is:"$0 }'

结果:

read the line is:1

$ seq 5 | awk '{ getline; print "read the line is:"$0 }'

结果:

read the line is:2
read the line is:4
read the line is:5

结果解析:从上面两种情况的结果看,getline表示获取当前记录的下一行。

getline放在BEGIN语句块中:BEGIN语句块是在读取数据之前执行的, 此时当前记录是第一条记录之前,getline获取下一行,就获取了第一行,也就是数字1。剩下的记录则交给主语句块处理(本例中没有主语句块)。

getline放在主语句块中:主语句块是逐行读取记录,读取时当前记录是第一行,getline获取下一行记录,所以输出2,当前记录转到第三行,再getline,则获取第四行的 4,当前记录转到5,到了文件末尾,没有下一行,print $0输出当前记录内容,所以输出5。

实例8:从awk中读取命令输出。awk可以调用命令并读取输出,把命令放入引号中,然后利用管道将命令输出传入getline

"command" | getline output;

例如:在/etc/passwd文件中查找包含“root”关键字的记录,然后显示出用户登录名(第一个字段)及其主目录(第六个字段)。/etc/passwd文件中的每条记录,是以冒号作为分隔符的。

$ echo | awk 'BEGIN { FS=":" } { "grep root /etc/passwd" | getline;print $1,$6 }'

结果:

root  /root 

 实例9:除了数字和字符串类型的变量,awk还支持关联数组。

        (关联数组是一种使用字符串作为索引的数组)。在awk中使用for循环和C语言类似:

$ echo | awk '{ for(i=0;i<10;i++) { print i; }}'

 awk中显示数组array内容的for循环还可以这样写:

$ for (i in array) { print array[i]; }

 例如,我们创建一个test.txt文件,内容如下:

110:first:1
111:second:2
112:third:3

        现在从这个文件中读取文本行,以":"作为分隔符,创建一个关联数组,数组的索引对应第一列,对应的值是文件的第三列,这样,将收集到的数据存入数组并打印出来。

$ awk 'BEGIN { FS=":" } { name[$1]=$3 } END { for(i in name) { print i,name[i] } }' test.txt
#创建的数组为name

结果:

111 2
110 1
112 3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值