文本处理三剑客:grep系,sed,awk grep系:grep,egrep,fgrep,基于PATTERN进行文本过滤; sed:流编辑器,逐行编辑器;模式空间,保持空间; awk:报告生成器;格式化文档输出;

下面是三剑客其中之一的awk:

awk:其实就是gawk,是一种模式扫描和处理语言(Centos系/RHEL系中是经过GNU重新编写过的awk叫做gawk); 格式:gawk [ options ] 'program' file ... program:[/PATTERN/]{ACTION statement;...} //只能有一对单引号,只能在program两侧 PATTERN部分:决定动作语句何时触发以及通过什么事件来触发; BEGIN,END 注意:在有BEGIN语句块的前提下,后面的file有时候可以省略; ACTION statement:对数据进行特定的处理,通常放置在{}中,并使用单引号进行引用;

	基本概念:
		分隔符:
			输入分隔符:awk对数据进行处理时,会根据特定的符号对数据进行分段处理,这种标识符号就称为“输入分隔符”,默认的输入分隔符是空白字符;如果指定的分隔符并没有被数据包括,则只有换行分隔符有效;
			输出分隔符:awk对数据处理完成后,会以特定的标识符号对各个字段进行连接后输出,这种标识符号就称为“输出分隔符”,默认的输出分隔符是空白字符;
		记录:由换行符进行分隔的数据中的一行,就称为一条记录(record);通常在使用awk处理数据时,使用$0来保存整个记录的内容;
		字段:经过分隔符分隔之后的每一个数据分段,都称为一个字段(field);通常在使用awk处理数据时,使用$1,$2,...$NF等内置变量来存储各个字段的数据;

awk的工作原理:
	1.首先,执行BEGIN{ACTION statement;...}语句块中的语句;
	2.其次,从文件中或者标准输入读取一行,再根据PATTERN的匹配结果执行后面的ACTION语句块中的内容;然后逐行重复该过程以完成数据处理,直至数据全部被读取完毕;
	3.最后,再所有的语句块都执行完成之后,退出awk进程之前,执行END{ACTION statement;...}语句块中的语句;
	注意:
		1)BEGIN语句块在awk开会处理数据内容之前就被执行;通常用于生成表头;此语句块是可选语句块;
		2)END语句块在处理完所有的数据之后,才会被执行;通常用于数据汇总;此语句块是可选语句块;
		3)PATTERN语句块中的通用命令是最重要的部分,因此PATTERN语句块不能省略,但其中的ACTION可以省略,如果省略,则默认执行print动作,即:显示数据的各行;
		4)awk在执行PATTERN语句块时,默认循环遍历数据汇总的各个记录;
  
常用选项:
	-f,--file program-file:从指定的文件中加载program语句块,而不是通过命令行给出相关的程序内容;
	-F,--field-separator fs:指定字段的输入分隔符;默认是空白字符;
	-v,--assign var=value:用于声明自定义变量或数组并为变量赋值;//可以在BEGIN、PATTERN和END语句块中自定义变量,也可以声明数组;在选项当中每定义一个变量都需要再书写一次"-v"

awk的常用用法:
	1.变量:
		内建变量:
			FS:输入字段分隔符,默认是空白字符;
			OFS:输出字段分隔符,默认的是空白字符;
				示例:
					~]# awk -v FS=':' -v OFS=':' '{print $1,$3,$7}' /etc/passwd  //显示当前系统内存在用户的用户名、用户的uid、用户默认的shell(用“:”输入分隔,使用“:”输出分隔)
					root:0:/bin/bash
					bin:1:/sbin/nologin
					daemon:2:/sbin/nologin
			RS:输入记录(行)分隔符,默认为换行符;  //换行符还是生效的
			ORS:输出记录分隔符,默认为换行符;  //awk认为的换行符,真正输出换行的还是原有的换行符
				示例:
				~]# awk -v RS=':' -v ORS='#' '{print $0}' /etc/passwd
				root#x#0#0#root#/root#/bin/bash
				bin#x#1#1#bin#/bin#/sbin/nologin
			NF:每一行当中字段的总数;
				示例:
				~]# awk -F: '{print NF}' /etc/passwd  //显示指定文件中每行中的字段的数量
				7
				...
				~]# awk -F: '{print $NF}' /etc/passwd  //显示指定文件中每行中最后一个字段的内容
				/bin/bash
				/sbin/nologin
				...	
				~]# awk -F: '{print $(NF-1)}' /etc/passwd  //显示指定文件中每行中倒数第二个字段的内容
				/root
				/bin	
				...
			NR:行的总数;如果仅处理一个文件,可以将NR的值当做文件中各行的行号;
				示例:
					~]# awk '{print NR}' /etc/fstab  //显示一个文件的行数,处理单个文件时,可以记做行数
					1
					2
					3
					...
			FNR:对于不同的文件分别统计其行的数量;即使处理多个文件,也可以显示每个文件中的行号;
				示例:
					~]# awk '{print FNR}' /etc/fstab /etc/issue //处理多个文件,显示每个文件中的行号
			FILENAME:当前正在被处理的文件的文件名;
				示例:
				~]# awk 'END{print FILENAME}' /etc/passwd 
				/etc/fstab
			ARGC:命令行中参数的数量;包括awk命令本身但不包括awk选项部分也不包括program部分;
				示例:
				~]# awk 'END{print ARGC}' /etc/fstab /etc/issue /etc/passwd
				4
			AGRV:由命令行中所有的参数构成的数组;AGRV[0]:显示参数构成数组中的第一个参数awk;
				示例:~]# awk 'END{print ARGV[1]}' /etc/fstab /etc/issue /etc/passwd  //显示命令行中的第N个参数
					  /etc/fstab
					  
		自定义变量:
			定义方式:-v var_name=value (变量字母严格区分大小写)
				示例:
				~]# awk -v var='hello' -F: '{print var","$1}' /etc/passwd  //对当前系统内的用户打招呼:“hello,User”
				hello,root
				hello,bin

awk常用的ACTION:
	2.print:以标准格式输出文档;
		格式:
			print item1,item2,...
				示例:
				~]# awk '{print}' /etc/issue
				CentOS release 6.7 (Final)
				Kernel \r on an \m
				~]# awk '{print $1,$3,$NF}' /etc/issue
				CentOS 6.7 (Final)
				Kernel on \m
			注意:
				1)各个元素之间需要使用“,”进行分隔;
				2)输出的各item可以是字符串,可以是数字,可以是当前记录中的字段,可以是变量,可以是awk的表达式;
				3)如果省略了item,则默认的是item为$0,即:输出整行;
	3.printf:以特定的格式输出结果;
		格式:
			printf "FORMAT" item1,item2,...
				示例:
					~]# awk -F: '{printf "%20s:%-+5d\n",$1,$3}' /etc/passwd
					                root:+0   
					                bin:+1   
					              	daemon:+2 
			注意:
				1)必须给出合适的输出格式;
				2)默认不会自动换行,如果想要在输出结果中换行显示的话需要明确的给出换行控制符“\n”;
				3)FORMAT中需要为后面的每一个item单独指定一个格式化符号;
			常用的FORMAT:
				%c:以ASCII码中的内容显示字符信息;
				%d,%i:显示十进制整数;
				%e,%E:以科学计数法来显示数字(浮点类型1.2,1.3,...);//相似:%g,%G:以科学计数法显示浮点数字
				%f,%F:显示十进制数字的浮点形式;
				
				%u:无符号的十进制数;
				%o:无符号的八进制数;
				%s:显示字符串;
				%x,%X:显示无符号的十六进制数;
				%%:显示一个“%”符号;
			修饰符:
				#[.#]:第一个数字用来控制显示宽度;第二个数字表示小数点后的精度;
				-:表示采用左对齐方式显示,默认是右对齐;
				+:显示数字的正负符号;(如果不是数字没有意义)
	4.操作符:
		算数运算操作符:(双目运算符优先级低于单目运算符)
			双目运算符:x+y,x-y,x*y,x/y(y不能为0),x^y(优先级最高),x%y
			单目运算符:
			    -x:将正整数转换为负整数;
			    +x:将字符串转换为数值;
			示例:~]# awk -F : 'END{print 100^2}' /etc/passwd
		字符串操作符:
			无任何操作符号,即为字符串连接操作;
		赋值操作符:
			=,+=,-=,*=,/=,^=,%=
			++,--(步长为1)
		比较操作符:
			==,!=,>,<,>=,<=
				示例:
					[root@chenliang ~]# awk -F: '$3==0{print $1}' /etc/passwd  //取出系统中用户uid为0的用户
					root
		模式匹配操作符:
			~:操作符左侧的字符串是否能够被右侧的模式PATTERN所匹配;
			!~:操作符左侧的字符串是否不能够被右侧的模式PATTERN所匹配;
				示例:
				~]# awk -F : '$NF~/bash/{print $0}' /etc/passwd  //取出系统中shell为bash的用户
		逻辑运算操作符:&&  || !
				示例:
					~]# awk -F: '$3>=100&&$3<=1100{print $0}' /etc/passwd  //取出系统中用户uid大于等于100且小于等于1100的用户
					usbmuxd
					rtkit
		条件表达式:
			selector(condition)?if-true-expression:if-false-expression
				示例:
					~]# awk -F: '{$3>=1000?usertype="Common User":usertype="SuperUser or Sysuser";printf "%20s: %-20s\n",usertype,$1}' /etc/passwd   //系统中用户uid大于等于1000就标记为Common User,否则标记为SuperUser or Sysuser
					Super or Systemuser: root                
					Super or Systemuser: bin
	5.PATTERN(模式)部分:
		1)empty:空模式,不加区分的处理文件的每一行;
		2)[!]/REGEXP/:仅处理[不]能被PATTERN匹配到的每一行;
			示例:~]# awk -F: '!(/^r/)&&/n$/{print $1}' /etc/passwd  //取出系统中非r开头且不以n结尾的用户名
				  bin:x:1:1:bin:/bin:/sbin/nologin
		3)关系表达式:
			示例: ~]# awk -F: '{$3>=1000?usertype="Common User":usertype="SuperUser or Sysuser";printf "%20s: %-20s\n",usertype,$1}' /etc/passwd   //系统中用户uid大于等于1000就标记为Common User,否则标记为SuperUser or Sysuser
					Super or Systemuser: root                
					Super or Systemuser: bin
		4)行域,行范围:
			关系表达式的逻辑运算:FNR>=10&&FNR<=20
				示例:
					~]# awk -F: 'FNR>=12&&FNR<=14{print $1}' /etc/passwd  //取出/etc/passwd中12到14行的用户名
					games
					gopher
					ftp
			/REGEXP1/,/REGEXP2/:从被REGEXP1匹配的行开始,直到被REGEXP2匹配的行结束,这期间的所有行;凡是属于此类的匹配结果,有多少组就显示多少组;
				示例:
					~]# awk -F: '/^n/,/^c/{print $1}' /etc/passwd  //取出/etc/passwd中从n开头到c开头为用户名的用户名
					nobody
					nfsnobody
					ntp
					cl1
		5)BEGIN/END模式:
			BEGIN{}:仅在开始处理文件中第一行文本数据之前执行一次的语句块;多用于输出特定格式的表头信息;
				示例:
					~]# awk -F: 'BEGIN{printf "%20s %10s %20s\n","USERNAME","USEUID","SHELL"}NR>=15&&NR<=20{printf "%20s %10s %20s\n",$1,$3,$7}' /etc/passwd   //在文件/etc/passwd中显示USERNAME ,USERUID ,SHELL为表头的15到20行的用户信息
					USERNAME    USERUID                SHELL
		              nobody         99        /sbin/nologin
		                dbus         81        /sbin/nologin
		             usbmuxd        113        /sbin/nologin
		                vcsa         69        /sbin/nologin
		                 rpc         32        /sbin/nologin
		               rtkit        499        /sbin/nologin
			END{}:仅在文本处理完成但awk命令尚未退出时执行一次的语句块;多用于数据信息的汇总;
				示例:
					~]# awk -F: 'BEGIN{printf "%20s %10s %20s\n","USERNAME","USERID","SHELL"}NR>=15&&NR<=20{printf "%20s %10s %20s\n",$1,$3,$7}END{print "----------------------------------------\n",NR " users"}' /etc/passwd
			注意:
				1)BEGIN语句块,PATTERN语句块,END语句块的顺序,通常的书写顺序为:BEGIN{}PATTERN{}END{};
				2)BEGIN语句块和END语句块是可选的,但PATTERN语句块必须要写;
		6)常用的ACTIONS
			1.表达式
			2.组合语句
			3.输入语句
			4.输出语句
			5.控制语句
		7)控制语句:
            (1)if ... else:
            	语法:if (condition) statement [ else statement ]
            	使用场景:对awk取得的整行或某个字段做条件判断;
            		示例:
            			~]# awk -F: '{if($3>=1000){print $1}else{print "Username: ",$1}}' /etc/passwd  //uid大于等于1000的用户,如果不符合条件的用户就在用户名签名标识“Username: ”字样
            			Username:  root
						Username:  bin
            			~]# awk '/^[^#]/{if(NF==6){print}}' /etc/fstab   //输出/etc/fstab文件中非#号开头的以空白字符分隔的含有6个字段的行
            			/dev/mapper/vg_chenliang-lv_root /                       ext4    defaults        1 1
						UUID=ceeb9f75-73ea-412f-a33a-1704433e538b /boot                   ext4    defaults        1 2
						/dev/mapper/vg_chenliang-lv_swap swap                    swap    defaults        0 0
						tmpfs                   /dev/shm                tmpfs   defaults        0 0
						devpts                  /dev/pts                devpts  gid=5,mode=620  0 0
						sysfs                   /sys                    sysfs   defaults        0 0
						proc                    /proc                   proc    defaults        0 0

            			分析磁盘上各个文件系统的空间利用率:
							~]# df -h | awk -F% '/^\/dev/{print $1}' | awk '{if($NF>=80){print $1}}'
							9
            (2)while循环:
            	语法:while (condition) statement
            	使用场景:
            		a.对一行内的多个字段逐一做相同或类似的操作处理时使用;
            		b.对数组中的各数组元素做遍历处理时使用;
            	while循环的特定:条件为真,进入循环;一旦条件为假,则退出循环;
            		示例:
            			~]# vim testfile
						Hello! My Linux World! Let's go ~
						~]# awk '{i=1;while(i<=NF){print $i,length($i);i++}}' testfile  //判断testfile中以空白字符分隔的每个字段的字符数
						Hello! 6
						My 2
						Linux 5
						World! 6
						Let's 5
						go 2
						~ 1
            (3)do ... while语句:
            	语法:do statement while (condition) //意义:与while循环相同,但statement语句段至少被执行一次;
            		示例:
            			~]# awk '{do{print $i,length($i);i++}while(i<=NF)}' testfile  //判断testfile中以空白字符分隔的每个字段的字符数
						Hello! My Linux World! Let's go ~ 33
						Hello! 6
						My 2
						Linux 5
						World! 6
						Let's 5
						go 2
						~ 1
            (4)for循环:
            	语法:
            		for (expr1; expr2; expr3) statement //第一种语法
            			expr1:变量赋初始值;
            			expr2:循环条件判断;
            			expr3:变量值的迭代处理(变量值修正方法);
            				示例:
								~]# awk '{for(i=1;i<=NF;i++){print $i,length($i)}}' testfile  //判断testfile中以空白字符分隔的每个字段的字符数
								Hello! 6
								My 2
								Linux 5
								World! 6
								Let's 5
								go 2
								~ 1
            		for (var in array) statement //第二种语法
            			示例:
            				awk -F: '$NF!~/^$/{BASH[$NF]++}END{for(i in BASH){printf "%15s:%i\n",i,BASH[i]}}' /etc/passwd
            				/sbin/shutdown:1
						     	 /bin/bash:2
						  	/sbin/nologin:29
						     	/sbin/halt:1
						     	 /bin/sync:1
				
            (5)switch ... case语句:
            	语法:switch (expression) { case value1|regex1:statement;case value2|regex2:statement; ... [ default: statement ]}
            	使用场景:用于字符串比较判断;
            (6)break语句和continue语句:
            	break [n]
            		示例:
            			[root@chenliang ~]# awk '{for(i=1;i<=NF;i++){if(length($i)<5){break}{print $i,length($i)}}}' testfile 
						Hello! 6
            	continue
            	注意:其使用场景是行内多个字段间做循环时的循环控制方式;
            		示例:
						~]# awk '{for(i=1;i<=NF;i++){if(length($i)<5){continue}else{print $i,length($i)}}}' testfile
						Hello! 6
						Linux 5
						World! 6
						Let's 5
            	
            (7)next语句:
            	在awk处理数据时,提前结束对当前行的处理,而直接开始处理下一行;
            		示例:
						~]# awk -F: '{if($3%2==1){next}else{print $1,$3}}' /etc/passwd
						root 0
						~]# awk -F: '{if($3%2==1)next;print $1,$3}' /etc/passwd
						root 0
            (8)数组——Array
            	用户自定义的数组,一般使用关联数字:array_name[index_expression]
            		注意:
            			1)index_expression可以使用任意的字符串,但字符串必须要放在双引号中;
            			2)支持弱变量数组,即:如果某数组元素事先不存在,当引用该元素时,awk会自动创建此元素,并为此元素赋“空字符串”作为其初始值;
		            		示例:			            			
								1.[root@chenliang ~]# awk 'BEGIN{name["leader"]="zhangsan";name["mem1"]="lisi";name["mem2"]="wangwu";print "Leader: ",name["leader"],"\nMember: ",name["mem1"],name["mem2"]}'
								Leader:  zhangsan 
								Member:  lisi wangwu
								2.[root@chenliang ~]# awk 'BEGIN{name["leader"]="zhangsan";name["mem1"]="lisi";name["mem2"]="wangwu";for(i in name){print name[i]}}'
								wangwu
								zhangsan
								lisi
								3.查看当前系统上所有服务的不同TCP状态的连接数量的统计;
									~]# netstat -nalt | awk '/^tcp\>/{state[$NF]++}END{for(stat in state){printf "%15s: %-10d\n",stat,state[stat]}}'
									ESTABLISHED: 1         
     								LISTEN: 10
								4.用于统计本服务器web站点的每个用户的请求数值:
									~]# awk '{ipaddr[$1]++}END{for(ip in ipaddr){print ip,ipaddr[ip]}}' /var/log/httpd/access_log	
									172.16.0.1 3
									172.16.74.1 3
								5.用于统计本服务器web站点的UV值(访问量):
									~]# awk '{ipaddr[$1]++}END{for(ip in ipaddr){print ip,ipaddr[ip]}}' /var/log/httpd/access_log | wc -l
			(9)函数:
				内建函数:
					数值函数:
						rand():返回一个介于0-1之间的随机数;
							示例:
								[root@chenliang ~]# awk 'BEGIN{print rand() }'
								0.237788
						sqrt():对于指定的数值进行开二次方;
							示例:
								[root@chenliang ~]# awk 'BEGIN{print sqrt(5) }'
								2.23607
					字符串函数:
						length():计算给定字符串的长度;
							示例:
								[root@chenliang ~]# awk 'BEGIN{print length(123456789) }'
								9
						gsub(r, s [, t]):以r表示的模式来查找t表示的字符串中能够被匹配的内容,并将所有出现的内容替换成s表示的内容;
							示例:
								~]# awk 'BEGIN{filed="This is a test text !";gsub(/text/,"file",filed);print filed}'
								This is a test file !

						split(s, a [, r [, seps] ]):以seps作为分隔符,利用r表示的模式进行匹配,将s代表的字符串分割之后,保存在a表示的数组之中;
							示例:
								示例1~]# awk 'BEGIN{demo="learning linux is very happy ~";split(demo,DEMO," ");for(i in DEMO){print i,DEMO[i]}}'
								4 very
								5 happy
								6 ~
								1 learning
								2 linux
								3 is
								示例2~]# awk 'BEGIN{print split("123#456#678", myarray, "#")}'
								3

				自定义函数:
					在命令行中自定义函数:function name(parameter list) { statements }
					在脚本中自定义函数:
						function 函数名(参数表){ 
						函数体 
						} 
						function  f(p, q,     a, b)   # a and b are local
      					{
           					...
      					}

      				示例:
      					写两个函数计算最小和最大数,从main函数中调用这些函数(自定义函数示例转自http://www.yiibai.com/awk/awk_user_definede_functions.html):
      					# Returns minimum number
						function find_min(num1, num2)
						{
						  if (num1 < num2)
						    return num1
						  return num2
						}

						# Returns maximum number
						function find_max(num1, num2)
						{
						  if (num1 > num2)
						    return num1
						  return num2
						}

						# Main function
						function main(num1, num2)
						{
						  # Find minimum number
						  result = find_min(10, 20)
						  print "Minimum =", result
						  
						  # Find maximum number
						  result = find_max(10, 20)
						  print "Maximum =", result
						}

						# Script execution starts here
						BEGIN {
						  main(10, 20)
						}

						在执行上面的代码后,会得到如下结果:
						Minimum = 10
						Maximum = 20