【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础

目录:
(一)AWK的工作流程及基本语法
(二)AWK操作符的使用
(三)AWK中BEGIN和END的用法
(四)常见的内置变量
(五)判断和循环语句
(六)AWK中数组的使用
(七)AWK中其他常用函数
(八)AWK格式化输出


AWK是一个优良的文本处理工具,Linux及Unix环境中现有的功能最强大的数据处理引擎之一。这种编程及数据操作语言(其名称源自于它的创始人Alfred Aho、Peter Weinberger和 Brian Kernighan三人姓氏的首个字母)的最大功能取决于一个人所拥有的知识。Awk经过改进生成的新的版本有nawk、gawk,现在默认的Linux系统下日常使用的是gawk,用命令可以查看正在应用的awk的来源(ls -l `which awk`)
(一)AWK的工作流程及基本语法
(1.1)我们的系统中例如有一个需要处理的文件file,文件共有n行,当我们使用awk时,首先会读取文件的第一行内容到内存中,然后会根据某种分隔符进行分段,awk默认的分隔符是空格和Tab,此时第一行被分隔成$1、$1、$2、$3、$4...其中这一整行是$0。第一行信息被读入内存并分隔后按照我们的需求做相关的操作,相关处理完成后便会把结果输出到屏幕上,然后依次读取下一行信息。
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(1.2)AWK的语法一般为“awk ‘{print $1,$2}’ file”这个格式,其中awk是默认开始部分,print $1,$2表示打印当前行的第1和第2个参数,file表示文件名。
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(1.3)我们首先在系统中创建一个aa.txt的文本文件,并在文件中编辑相关的信息,然后使用awk打印出文本中每行第1和第2个参数的信息。由于awk是行文本编辑器,所以awk在处理完第一行的信息后,便将第一行的信息从内存中删除,然后读取下一行的内容到内存,然后在处理此行内容的相关输出。
# awk '{print $1,$2}' aa.txt
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(1.4)以上是对文本中所有的行进行编辑的操作,如果我们希望对含有特定信息的某些行进行操作,则应该使用如下的格式操作“awk ‘/模式/{print $1,$2}’ file”,如果我们需要对aa.txt文本中有root字段的行进行处理,则可以在模式中添加对应的root字段。
# awk '/root/{print $1,$2}' aa.txt
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(1.5)我们知道在awk中默认的分隔符是空格和Tab,但是有时候一些文本文件的分隔符并不完全是以空格和Tab作为分隔符的,例如我们在系统创建了一个passwd文件,这个文件就是以“:”冒号作为分隔符的,此时我们应该在awk中使用“-F”指定分隔符,然后在去进行相关的文本处理操作。
# head -5 /etc/passwd > passwd
# awk -F: '{print $1}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(1.6)有时候文本中的分隔符并不是一种,可能有多种分隔符同时存在例如冒号和逗号,我们就应该在awk中设置以两种符号都作为分隔符,此时仍然能正常的打印我们每行的第1个参数。
# awk -F'[:,]' '{print $1}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(1.7)例如使用创建的aa.txt文件,我们在awk中还可以使用$0表示所打印的那一整行的信息。
# awk '/root/{print $1,$2,$3,$4}' aa.txt
# awk '/root/{print $0}' aa.txt
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(1.8)如果我们现在想知道当前系统有哪些用户在登录,那么我们可以先使用who命令进行查询,然后再由awk进行过滤。
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础


(二)AWK操作符的使用
一般在awk中使用的操作符主要有:~、!~、==、!=、>、>=、<、<=、&&、||、++、--等这样的一些形式
(2.1)一般使用“awk ‘/模式/{print $1,$2}’ file”这个进行过滤的时候,例如选择模式为root,那么便会过滤出文本中每一行只要出现root字段就符合条件显示出来行。但是如果在一个文本文件中,我们只希望某一列包含有root字段时才过滤,则应该使用“~”
# awk -F'[:,]' '/root/{print $0}' passwd
# awk -F'[:,]' '$5~/root/{print $0}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(2.2)如果我们想匹配所有第5列没有root字段的行,则可以使用“!~”,我们使用“$5!~”进行匹配,此时第1行和第5行的第五个字段是root,所以不会被匹配,所以我们看到第2、3、4、6、7行都被打印出来了。
# awk -F'[:,]' '$5!~/root/{print $0}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(2.3)注意,在//这个双斜线之间的信息是可以写正则表达式的,例如/r.../表示的是以“r”字母开头,后面有三个任意字符都是符合条件的。
# awk -F'[:,]' '$5~/r.../{print $0}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(2.4)如果我们在对文本进行过滤时能够完全匹配,此时需要使用“==”且将关键字用双引号引起来,表示我们需要匹配第5个字段是完全等于root的行过滤出来并打印到屏幕上。
# awk -F'[:,]' '$5=="root"{print $0}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(2.5)如果我们希望第5个字段不等于root的时候能够匹配出来,则应该使用“!=”,此时发现只有第1行没有被匹配出来。
# awk -F'[:,]' '$5!="root"{print $0}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(2.6)现在我们希望匹配出所有在第3个字段大于等于2的行,则可以使用“>=”
# awk -F'[:,]' '$3>=2{print $0}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(2.7)如果我们希望能够通过两个条件来过滤数据,则可以使用“&&”或者“||”操作符。例如现在我们希望第5个参数包含root同时第3个参数小于等于2,则可以通过“&&”操作符实现;如果希望第5个参数等于rootx或者第3个参数小于等于2两个条件成立一个都能显示到屏幕,则可以通过“||”符号实现。
# awk -F'[:,]' '$5~/root/&&$3<=2{print $0}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(2.8)如果我们希望第3行的信息全部加1后打印在屏幕,则可以使用“++”或者“+=”号实现;我们希望第3行信息全部减1后打印在屏幕,则可以使用“--”或者“-=”符号实现。{++S[$NF]}:见文章《【我的Linux,我做主!】使用netstat监控网络连接信息》
注意:操作命令都要写在{}里,如果{}出现多个命令的话,每个命令用“;”分号分隔。
# awk -F'[:,]' '{$3++;print $3}' passwd
# awk -F'[:,]' '{$3+=1;print $3}' passwd
# awk -F'[:,]' '{$3--;print $3}' passwd
# awk -F'[:,]' '{$3-=1;print $3}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础


(三)AWK中BEGIN和END的用法
我们使用awk的时候,是将文本信息一行一行的读取到内存中操作的。那么我们是否可以在读取文件的第一行之前先做其他的准备工作,准备工作完成之后再从第一行的内容开始读取,同时在整个文件读取完成之后,我们还想继续做一些后续的扫尾操作,然后再让执行的程序结束。其实这些要求我们都是可以实现的,我们通过使用BEGIN和END就可以很好的处理这些问题。
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(3.1)我们希望将passwd文件中先打印一行横线,然后把所有的用户名打印出来,此时可以使用BEGIN参数
# awk -F'[:,]' 'BEGIN{print"----------------------"}{print $1}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(3.2)我们还可以在BEGIN中设置变量,此时我们不用去指定分隔符“-F'[:,]'”了,可以直接在BEGIN中使用FS的变量去设置。注意,在使用FS变量的时候,后面的变量如果不是数字就一定要用双引号引起来,否则会出现报错。
# awk 'BEGIN{FS="[:,]"}{print $1}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(3.3)我们需要知道在引用变量的时候,直接引用即可,变量前面是需要不加$等字符引用的。例如我们定义了一个aa,当在print后直接使用aa,表示打印1,如果使用$aa,表示打印S1,即仍然是打印文档中的每行第一列的信息。
# awk 'BEGIN{FS="[:,]";aa=1}{print aa}' passwd
# awk 'BEGIN{FS="[:,]";aa=1}{print $aa}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(3.4)我们希望在读取完最后一行后还可以有其他的操作行为,此时可以使用END操作符。例如现在我们要求将passwd文件每行的第三列全部相加,然后在屏幕只打印最后一个相加的结果,在图1-35中我们在操作栏中定义了将每行的第三个字段进行相加,此时在屏幕上没读取一行便会打印一个相加的结果值,但是这个现实方式并不是我们想要的结果。此时我们使用END操作符,待相加的循环操作全部执行完毕后再将aa的值打印到屏幕上(图1-36)。
注意:如果是END后的操作1和操作2是在一个print后执行的,那么就是用逗号“,”隔开,如果是多个print分开执行的,仍然是用分号“;”隔开。
# awk 'BEGIN{FS="[:,]"}{aa+=$3;print aa}' passwd
# awk 'BEGIN{FS="[:,]"}{aa+=$3}END{print "所有的UID相加的和是",aa}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础


(四)常见的内置变量
在awk中常见的内置变量主要有:FS、OFS、NF、RS、ORS、FILENAME、NR、FNR等等。
(4.1)第一个FS:表示的是定义分隔符,“FS="[:,]"”表示的是在文本文件中每一行定义以冒号和逗号作为分隔符,这样我们就可以正常的打印出每一行第1个字段的信息。
# awk 'BEGIN{FS="[:,]"}{print $1}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(4.2)第二个OFS:表示的是输出的时候,可以通过这个内置变量定义输出字段的分隔符,“OFS="--------"”表示的是在屏幕显示输出的时候是以一行横线作为分隔符的,如果没有指定的话,默认是以空格作为换行符的。
# awk 'BEGIN{FS="[:,]";OFS="--------"}/root/{print $1,$2}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(4.3)第三个NF( Number of Fileds):表示的是统计每一行的所有字段的个数信息,并赋值到NF中再打印到屏幕上。
# awk 'BEGIN{FS="[:,]"}/root/{print NF}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(4.4)我们之前说了在awk中在引用变量的时候直接使用变量名即可,在前面是不需要加$等其他的字符的,所以当我们打印NF的时候就会打印每一行的字段个数,但是如果使用$NF打印时,我们就会发现,是会打印每一行最后一个字段的值的。
# awk '{print $NF}' aa.txt
# awk '{print $(NF-3)}' aa.txt
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(4.5)第四个RS:表示的是文本文件中行的分隔符,默认使用的分隔符是换行符“\n”。例如现在我们自己定义aa.txt文件中每一行是以横线作为换行符,但是在aa.txt文件中并不存在横线,所以awk会认为整个文件都是一行,屏幕上会按照原样打印出来。
# awk 'BEGIN{RS="-"}{print $0}' aa.txt
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(4.6)我们创建一个bb.txt文件,然后填写如图1-43的一行信息,我们希望将相关的姓名和职位信息过滤出来并显示在屏幕,我们通过指定bb.txt文件中打印的每行字段的位置信息如$2、$4、$6等,虽然也能显示正确的信息(图1-44),但是非常的不方便。此时我们就可以使用RS的内置变量来指定文本的行分隔符“RS=" "”,此时信息便被分隔成了3行,我们再打印第二字段的信息就可以正常的显示我们所需的信息了,非常的方便。
# awk 'BEGIN{FS="[,| ]"}{print $2,$4,$6}' bb.txt
# awk 'BEGIN{FS=",";RS=" "}{print $2}' bb.txt
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(4.7)第五个ORS:我们发现输出的时候行的分隔符和RS一致,也默认是“\n”换行符,而输出分隔符的内置变量则是ORS。例如我们指定ORS的分隔符为一行横线,此时在屏幕输出时就会以定义的横线连接每一行的内容并显示出来。
# awk 'BEGIN{ORS="--------"}{print $0}' aa.txt
# awk 'BEGIN{ORS="\n----------\n"}{print $0}' aa.txt
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(4.8)第六个FILENAME:表示的是当前文本文件的文件名,当我们指定FILENAME的时候,会将文本文件的文件名打印出来,这样我们一共有7行,那么就会打印7行文件名passwd
# awk -F"[:,]" '{print FILENAME}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(4.9)第七个NR(Number of Record):表示的是当前文本的行号信息,对于aa.txt文件,使用NR的内置变量时会将每一行的行号打印出来(图1-49),当我们指定了行的分隔符为横线时,此时由于aa.txt文件中并没有横线,所以只会显示数字1这一行的行号。
# awk '{print NR}' aa.txt
# awk 'BEGIN{RS="-"}{print NR}' aa.txt
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(4.10)第八个FNR:表示的也是当前文本的行号信息,但是它和NR的区别在于,当同时对两个文件aa.txt和cc.txt文件进行处理的时候,NR会将两个文件合并起来显示行号,但是FNR虽然也是将两行信息进行了合并,但是仍然会分别给两段内容独立的行号。
# awk '{print NR,FNR,$0 }' aa.txt cc.txt
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(4.11)我们可以通过判断每一行的NR和FNR是否相等,从而来判断当前是在哪一个文件中,例如NR和FNR等于1,所以显示aa.txt,NR和FNR不相等时,所以会显示cc.txt。不过需要注意的是应该要先判断是否相等然后再判断是否不等这样操作比较符合正常的判断流程。
# awk 'NR==FNR{print NR,FILENAME,FNR}NR!=FNR{print NR,FILENAME,FNR}' aa.txt cc.txt
# awk 'NR!=FNR{print NR,FILENAME,FNR}NR==FNR{print NR,FILENAME,FNR}' aa.txt cc.txt
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础


(五)判断和循环语句
(5.1)有时候我们需要对查询的信息进行判断,此时我们就应该使用if语句;而有时候我们需要重复循环的执行某些操作,此时我们就可以使用for循环或者是while循环;有时候我们又需要对循环进行控制,此时我们可以使用break、next、continue
(5.2)首先我们看if语句的语法:如果语句1没有使用大括号,则应该使用分号分隔,反之如果有大括号则不需要使用分号分隔。在awk的语法中“awk ‘/模式/{操作}’ file”,if语句是写在“操作”的位置部分的。
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(5.4)除了使用if语句进行判断之外,我们也可以使用三目运算符进行判断操作。使用三目运算符进行操作,判断第三个字段如果等于0则代表是root,如果不等于0则代表不是root。
# awk -F"[:,]" '{print ($3==0?"是root":"不是root")}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(5.5)接着我们来了解for循环的语法:其中for循环第一部分包含变量的初始值,变量的范围,变量的增长幅度,第二部分则是命令。
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(5.6)现在我们希望将passwd文件中每一行的NF-5个数的字段打印到屏幕上并在打印字段前打印一行横线,则可以通过如下的命令实现。
# awk -F"[:,]" '{
\> print "------------"
\> for(i=1;i<=NF-5;i++){print $i}
\> }' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(5.7)实战练习:我们创建一个dd.txt的文件,然后编辑如下的信息,现在我们希望将文本文件中所有以.pl结尾的格式字符全部找出来。可以使用for循环先将每一行进行循环,然后使用if条件判断是否符合要求,最终将所有的.pl结尾的格式字符打印出来。我们发现.pl文件的格式都是以“/xy/x任意数字.pl”形式出现。
# awk '{for(i=1;i<=NF;i++){if($i~/\/xy\/x[0-9]+.pl/){print $i;next}}}' dd.txt
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(5.8)接着我们来看while语句的语法:while语句首先会执行一个判断,如果判断是成立的则会去执行后面的语句,然后再返回到判断中,如果仍然成立那么就继续执行语句,直到判断不成立时,执行的语句不在执行跳出循环。
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(5.9)现在我们的需求是使用while语句让每一行的字段逐行打印同时还要让i值自增加,并且打印前会先执行一个打印横线的操作。
# awk -F"[:,]" '{print "-------------";i=1;while(i < NF){print $i;i++}}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(5.10)有时候我们在执行循环的过程中,可能会出现当符合某个条件后那么就不要再继续循环了,此时我们应该使用循环控制的关键字,例如:break、continue、next。
(5.11)首先我们来了解break:其中break关键字只能用在循环语句里,如果遇到break的话,那么就跳出循环。使用echo -e表示会识别换行符“\n”,使用for循环将每行的字段读入,然后使用if判断,当值小于2的时候会打印出来,当值大于等于2的时候则不执行后面的print操作并break跳出for循环,进入下一行的读入处理
# echo -e "1 2 3 1 4\n5 6 7 1" | awk '{for(i=1;1<=NF;i++){if($i>=2){break}print $i}}'
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(5.12)接着我们来了解continue:其中continue也是只能出现在循环语句里,如果遇到continue的话,那么continue就不在执行了,进行当前行的下一轮循环。当我们使用continue的时候第一行第1个字段被打印出来,此时for循环继续在这一行执行,当第三个字段不符合要求没有打印出来后,会继续执行第一行第4个字段的比较,由于输出的分隔符默认是“\n”换行符,所以最终打印了三行字段(图5-11)。
# echo -e "1 2 3 1 4\n5 6 7 1"|awk '{for(i=1;i<=NF;i++){if($i>=2){continue}print $i}}'
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(5.13)接着我们来了解next:其中的next的效果和break是基本一致的,如果遇到了next的话,会跳出循环,然后读取下一行。一般break和continue只能用于循环语句里执行,next除了能在循环语句中执行,还可以直接在一般的判断语句中执行。
# echo -e "1 2 3 1 4\n5 6 7 1" | awk '{for(i=1;i<=NF;i++){if($i>=2){next}print $i}}'
# echo -e "1 2 3 1 4\n5 6 7 1" |awk '{if($2<=2){next}print $0}'
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(5.14)我们也可以使用next关键字实现条件判断的作用,我们对aa.txt和cc.txt合并文件进行处理,接着使用next关键字,此时可以实现判断的效果,如果NR等于FNR的值则打印当前的文件名并跳出循环,如果NR等于FNR为错,则直接执行第二段print,打印对应的文件名。
# awk 'NR==FNR{print NR,FILENAME,FNR;next}{print NR,FILENAME,FNR}' aa.txt cc.txt
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础


(六)AWK中数组的使用
数组在awk中是非常重要的一个概念,很多时候我们在进行统计的操作或者是进行文件合并的时候都会用到数组。一般我们在定义变量的时候使用是“变量名=值”如果再次赋值的时候就会将上一个值覆盖,即一个变量同时只能有一个值。如果我们想同时对一个变量赋多个值,此时我们就可以使用数组解决,数组通过使用下标来对应不同的变量值,定义数组的过程就是定义数组下标的过程,数组的下标可以是数字也可以是字母,如果是数字的话不一定非要是连续的,如果是字母的话,必须要使用””双引号引起来。
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(6.1)我们可以通过手工的方式来定义数组,例如在数组中定义了三个值,然后打印第二个定义的值,是可以正常的打印出定义的数值的。
注意:在定义变量的时候a[“xx”],如果使用的下标是字母,那么我们是需要加上双引号的,如果字母没有加上双引号,那么中括号中会认为是NULL值,这样打印的结果只会是最后一次赋的值3(图6-3),如果在赋值的时候,所赋的值不是数字,而是字母,那么也应该使用双引号将所赋值的字母括起来,否则其值也会显示为NULL(图6-4)
# echo '' | awk '{a["xx"]=1;a["yy"]=2;a["zz"]=3;print a["yy"]}'
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(6.2)现在我们定义了一个数组,那么现在如何判断一个下标是否是出现在数组中的呢?判断数组下标是否是在数组中的,“aa”作为下标不在数组中,所以没有任何的显示,“yy”作为下标查询时发现是在数组中的,所以显示的结果是OK
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(6.3)以上是手工定义数组的方式,第二种定义数组的方式是读取文件的某部分,我们先创建两个文件c1和c2,我们需要将c1文件和c2文件进行合并操作,找到共同的部分,把这个共同的部分$1作为下标赋值给一个数组的元素。其中$0表示整行的意思。
a[021]=”021 上海”
a[010]=”010 北京”
a[025]=”025 南京”
# awk 'NR==FNR{a[$1]=$0}NR!=FNR{print a[$1],$2}' c1 c2
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(6.4)接着我们对c2文件进行修改,修改成如下的形式(图6-10),此时我们在NR等于FNR的过程中将c1文件的内容赋值到数组a中,由于此时数组的下标和c2文件中第2个字段是一致的,所以我们在比较NR不等于FNR的过程中数组中使用$2来引用,此时便仍然可以打印出我们所需的合并结果。
# awk 'NR==FNR{a[$1]=$0}NR!=FNR{print a[$2],$1}' c1 c2
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(6.5)有时候我们想要遍历数组中所有的元素,此时我们可以使用for循环来处理,我们使用“for(i in a)”,这样可以使用定义的i去遍历数组a中的所有下标,然后在去执行相关的操作即可。
# echo ''| awk '{a["xx"]=1;a["yy"]="aa";a["zz"]=3;for(i in a){print a[i]}}'
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(6.6)当然在执行遍历操作之前我们是可以使用delete的删除命令的。
# echo ''| awk '{a["xx"]=1;a["yy"]="aa";a["zz"]=3;delete a["xx"];for(i in a){print a[i]}}'
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础


(七)AWK中其他常用函数
在awk中为我们提供了很多的函数,通过这些函数便可以快速的实现我们想要实现的功能,常用的函数主要有:sub、gsub、length、substr、match、split、getline
(7.1)第一个sub:第1种语法为sub("old","new"),表示的是将旧的字段替换成新的字段;第2种语法为sub("old","new",目标),表示的是将旧的字段替换成新的字段,并且指定是哪一个字段进行替换。
# awk '{sub("tom","TOM");print $0}' ee.txt
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(7.2)第二个gsub:如果我们想要将文本文件中所有符合条件的字符都进行替换,则应该使用gsub进行全局替换。
# awk '{gsub("tom","TOM");print}' ee.txt
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(7.3)现在我们有一个需求,需要将sar命令统计出来的时间格式转换为自1970年以来一共经历了多少秒的格式,因为有时候我们在使用一些工具进行绘图时,比如rrdtool、gnuplot的时候,这类工具只能识别转换为自1970年以来一共经历了多少秒的时间格式(# date +%s)。我们通过使用时间的转换命令将时间格式转换成对应的需求格式,然后使用sub函数将文件的第一个字段进行替换。或者使用常规的替换方式$1=xx也是可以的。
# LANG=C sar -u 1 5 > zz.txt---输出CPU的统计信息,每隔1秒一共统计5次
# date -d 15:21:07 +%s
# grep -P '^[0-9].+' zz.txt | awk '{"date -d "$1" +%s"| getline xx;sub($1,xx);print}'
# grep -P '^[0-9].+' zz.txt | awk '{"date -d "$1" +%s"| getline xx;$1=xx;print $0}'
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(7.4)第三个length:其中length是用来计算某个字符串的长度的,我们可以查看passwd文件中第一个字段的字符长度。
# awk -F"[:,]" '{print $1,length($1)}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(7.5)第四个substr:其中substr是用来截取一段数据的,例如在passwd文件中,我们想要截取每一行的第一个字段里从第二个字符开始到最后的信息(图7-9),如果我们想要每一行的第一个字段里从第二个字符开始的信息到指定个数的字符结束,则可以使用substr函数中的第三个参数来获取我们所需的指定的长度字符(图7-10)
# awk -F"[:,]" '{print substr($1,2)}' passwd
# awk -F"[:,]" '{print substr($1,2,2)}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(7.6)现在我们对c1文件进行修改后,再次实现合并c1和c2文件的需求。当NR等于FNR的时候,我们首先使用substr将c1中第一个字段从第二个字符开始截取放入到数组a中,并将第二个字段的内容赋值到每一个数组元素中,当NR不等于FNR的时候,我们把c2文件的字段和a数组的对应字段进行打印,此时便可以获得我们所需的文件显示格式。
# awk 'NR==FNR{a[substr($1,2)]=$2}NR!=FNR{print $2,a[$2],$1}' c1 c2
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(7.7)第五个match:其中match指的是某种格式的字符,在指定字符的第几个位置出现(图7-14)。
# awk '{xx=match($0,/\/xy\/x[0-9]+.pl/);print xx}' dd.txt
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(7.8)接着我们来看看match函数中的两个引用字段:RSTART和RLENGTH,其中RSTART表示的是指定的字段从当前行的哪一个字符开始的,而RLENGTH表示的是满足格式的字段的长度(图7-15)。此时我们使用match函数的RSTART和RLENGTH关键字,并配合substr函数便可以得到了文本文件中所有以.pl结尾的字段信息(图7-16)。
# awk '{xx=match($0,/\/xy\/x[0-9]+.pl/);print xx,RSTART,RLENGTH}' dd.txt
# awk '{xx=match($0,/\/xy\/x[0-9]+.pl/);print substr($0,RSTART,RLENGTH)}' dd.txt
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(7.9)第六个split:其中split指的是把指定的字符串,按照某种格式的分隔符,分割成数组。我们使用split函数,然后首先指定原始字符2019-10-03,然后指定数组名为xx,最后打印数组中的结果(图7-18)。我们在split函数中指定原始字符、数组名、和分隔符冒号,然后打印数组中分隔出的第一个字段(图7-19),可以基本实现和常规“-F[]”一致的用法。
# echo '' | awk '{split("2019-10-03",xx,"-");print xx[3]}'
# awk '{split($0,xx,":");print xx[1]}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(7.10)第七个getline:这个getline函数可以让我们使用操作系统命令,也可以读取另外的一个文件的内容。我们尝试使用getline读取本文件的下一行,首先awk先读取文本文件中第一行信息,接着getline读取了下一行的信息,并将整行信息打印出来,此时我们就得到了显示2和4行的信息(图7-21)。当我们使用getline xx的时候是把本文件的下一行赋值给xx,首先awk读取第一行的信息,然后getline获取第二行的信息并将第二行的信息赋值给了xx,所以此时打印内存的数据仍然是第一行(图7-22)。
# awk '{getline;print $0}' xx
# awk '{getline xx;print $0}' xx
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(7.11)我们也可以使用getline读取文件中的信息,现在我们的需求是从两个文件aa.txt和xx文件中依次读取一行信息,使得最后打印在屏幕上的效果是交叉的将两个文本的信息打印出来。
# awk '{print $0;getline aa<"xx";print aa}' aa.txt
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(7.12)我们在6.4和7.6中已经实现了文件的合并需求,现在我们使用getline再次实现合并不同文本文件的需求,我们首先使用awk对c2文件进行操作,使用getline读取c1的第一行文件赋值给xx,此时我们打印xx和c2文件的第一个字段,便可以实现文件合并的效果。
# awk '{getline xx<"c1";print xx,$1}' c2
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(7.13)我们还可以使用getline获取操作系统的命令,我们使用ehco命令打印一个空字符,然后对awk内部使用的操作系统命令进行处理;也可以使用awk中的BEGIN来操作,因为BEGIN还没有到达处理数据的那一步,所以此时可以不用跟具体的文件file也没有问题。此时我们便顺利的实现了在awk中执行操作系统CMD的需求。
# echo '' | awk '{"date" | getline xx;print xx}'
# awk 'BEGIN{"date" | getline xx;print xx}'
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(7.14)接着我们可以使用while循环语句搭配getline语句实现在awk命令中打印/boot目录下所有文件名的需求。所以有了在awk中执行系统命令的功能,此时我们便可以实现和用户交互的功能,我们从/dev/tty的系统终端中获取用户输入的信息。
# echo '' | awk '{while("ls /boot" | getline xx){print xx}}'
# echo '' | awk '{print "请输入您的名字";getline name<"/dev/tty";print "您好:"name}'
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础


(八)AWK格式化输出
(8.1)平常我们使用print语句在屏幕打印显示的时候,是非格式化输出,print本身是带有换行符的,当然我们也可以在print中使用其他的转义符如“\n”
# awk -F"[:,]" '{print $1,$3}' passwd
# awk -F"[:,]" '{print $1,"\n",$3}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(8.2)使用print的时候每一行打印完都会有一个自动换行符“$”,我们可以使用cat -A命令查看到相关的字符信息(图8-2)。我们也可以使用“\f”表示换页符,此时每一行的信息便会以一页一页的方式显示在屏幕上(图8-3)。
# awk -F"[:,]" '{print $1,$3}' passwd |cat -A
# awk -F"[:,]" '{print $1,$3,"\f"}' passwd | cat -A
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(8.3)在使用print的时候我们也可以使用“\b”,表示的是删除字符键,此时“\b”前面的一个单个字符会被删除然后打印在屏幕上。
# awk -F"[:,]" '{print $1"\b",$3}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(8.4)我们发现使用print来打印信息的时候排版起来版面非常的不美观,此时如果我们希望使用某种格式来格式化的方式来显示文本的话,我们可以使用printf关键字。例如我们在printf后的双引号中首先打印字符,再打印数字并加上一个换行符,此时打印在屏幕的结果便会完全按照我们的在双引号中指定的格式显示出来(图8-6)。不过在图8-6中的格式化打印仍然不够美观,排列不够一致,我们可以使用左对齐(-10)或者右对齐(+10)的方式解决,此处我们使用“%-10s”左对齐的方式让排版更加美观(图8-7)。
# awk -F"[:,]" '{printf "%s %d\n",$1,$3}' passwd
# awk -F"[:,]" '{printf "|%-10s|%d\n",$1,$3}' passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
(8.5)“%d”表示的是整型,所以如果有小数的情况也会自动转换为整数(图8-8)。如果我们想完整的将浮点数的内容显示出来,则可以使用“%f”,表示显示浮点型(图8-9)。如果想以科学计数法的方式显示结果,则应该使用“%e”来进行屏幕打印(图8-9)。如果想以格式化的方式打印文件,则可以使用打印字符加左对齐的方式来处理(图8-10),和图8-7的方式一致。
# echo '' | awk '{printf "%d\n",2*2.4}'
# echo '' | awk '{printf "%f\n",2*2.4}
# echo '' | awk '{printf "%e\n",2*2.4}'
# awk -F"[:,]" '{printf "%-20s%d\n",$1,$3}' /etc/passwd
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础
【讲清楚,说明白!】Linux从业人员必备工具(一)--AWK文本处理利器基础

—————— 本文至此结束,感谢阅读 ——————

猜你喜欢

转载自blog.51cto.com/13613726/2460917