awk报告生成器详解


AWK: Aho, Weinberger, Kernighan --> New AWK, NAWK   基本用法:gawk [options] 'program' FILE .. [options]为选项: -F:指明输入时用到的字段分隔符; -v var=value: 自定义变量; 'program' 可以理解为对文本的操作方法。 格式:PATTERN{ACTION STATEMENTS}  PATTERN,模式匹配,支持正则表达式。用于地址定界。 ACTION,操作命令,如print STATEMENTS,指操作命令输出的内容。 FILE ..;进行操作的文本文件。  
  • pint 操作
格式:print item1, item2, ... 此操作用于输出命令匹配到的内容。 要点: (1) 逗号分隔符; (2) 输出的各item可以字符串,也可以是数值;当前记录的字段、变量或awk的表达式; (3) 如省略item,相当于print $0; 实例: tail -5 /etc/passwd | awk -F: '{print $2,$4}'  分析: 1.awk默认以空白字符作为分隔符,空白字符的数量可以是一个或者多个。用选项-F可以指定分隔符。 2.awk对接收到的文本,会逐行读取,逐行解析。 3.对分隔后的字段,分别赋值给变量$1,$2,$3,.。 4.NF表示字段的数量,$NF表示最后一个字段。 5.'{print $2,$4}' 表示对字段$2和$4执行print操作。输出的字段用空格区分。   2、变量 2.1 内建变量 FS:input field seperator,输入字段分隔符,默认为空白字符; OFS:output field seperator,输出字段分隔符,默认为空白字符; RS:input record seperator,输入时的换行符; ORS:output record seperator,输出时的换行符; 实例: 1.awk -v FS=':' '{print $1}' /etc/passwd 把passwd文件,以冒号分隔,然后打印出第一个字段。 分析: 1.分隔符用变量FS进行赋值,FS为输入文件的分隔符。 2. -v FS=':' 的作用相当于 -F: 3.定义变量时需要加上-v  2.awk -v FS=':' -v OFS='#' '{print $1,$3,$7}' /etc/passwd 以冒号分隔字段,然后输出其中的字段,并且用#分隔。 分析: 1.输出分隔符用变量OFS进行赋值。 2.每个变量之前都要加上选项-v 其它一些变量: NF:number of field,字段数量 {print NF}, {print $NF} 分析:NF是字段数量,$NF是字段。 示例:]# awk -v FS=':' '{print NF}' /etc/passwd 以冒号分隔,打印其字段数量。 NR:number of record, 行数; 示例: 1.awk '{print NR}' /etc/fstab 分析:打印出文件中每一行的行号。 每取一行,就打印一次行号。 FNR:各文件分别计数;行数;   FILENAME:当前文件名;包括文件夹路径的全名, ARGC:命令行参数的个数; 示例: 1.awk 'BEGIN{print ARGC}' /etc/fstab 返回2. 分析: 1.返回此条命令的参数的个数。 2.此命令的第一个参数为awk 3.第二个参数为/etc/fstab, 4。'BEGIN{print ARGC}'算是awk的一部分。 ARGV:数组,里面保存的是命令行所给定的各参数; 示例: 1.awk 'BEGIN{print ARGV[0]}' /etc/fstab 返回awk 分析: 1.ARVG是当前命令的参数的数组。 2.ARGV[0]是当前命令的第一个参数。awk 3.ARGV[1]是当前命令的第二个参数。/etc/fstab  
  • 2.2 自定义变量
自定义变量的方法: (1) -v var=value 使用-v选项来定义,变量名区分字符大小写; 示例: 1. awk -v test='hello gawk' '{print test}' /etc/fstab 分析: 1.此命令为打印自定义变量,也就是文本hello gawk. 2./etc/fstab的作用仅仅是决定打印的次数。 2.awk -v test='hello gawk' 'BEGIN{print test}' 分析: 1.BEGIN,启用begin模式。 2.此模式下,仅运行一次命令,并且不需要文件名作为参数。    (2) 在program中直接定义 示例: 1.awk 'BEGIN{test="hello gawk";print test}' 分析: 1.把变量直接写在{}里面,这叫做在program中定义。 2.在program中定义,不需要加选项-v  
  • 3、printf命令
  格式化输出:printf FORMAT, item1, item2, ...   (1) FORMAT必须给出; (2) 不会自动换行,需要显式给出换行控制符,\n (3) FORMAT中需要分别为后面的每个item指定一个格式化符号;  
  • 格式符:
%c: 显示字符的ASCII码; %d, %i: 显示十进制整数; %e, %E: 科学计数法数值显示; %f:显示为浮点数; %g, %G:以科学计数法或浮点形式显示数值; %s:显示字符串; %u:无符号整数; %%: 显示%自身; 示例: 1.awk -F: '{printf "%s\n",$1}' /etc/passwd 分析: 1.printf 表示使用格式化输出。 2.program里面的""双引号里面的内容,表示格式符。 3."%s\n",$1 表示对字段$1的内容,套入"%s\n"的格式中。即显示字符串,并且换行。 2.awk -F: '{printf "Username:%s\n",$1}' /etc/passwd 分析: 1."Username:%s\n",里面的Username:会直接显示出该字符。  
  • 修饰符:
#[.#]:第一个数字控制显示的宽度;第二个#表示小数点后的精度; 示例: 1. %3.1f 分析: 1."Userid:%3.1f\n"中的Userid:会直接显示字符本身。 2.%3.1f 中的3.1是修饰符,表示字符宽度3,1是小数点后1位,f是显示为浮点数。     2. awk -F: '{printf "Username:%15s\n",$1}' /etc/passwd 分析: 1."Username:%15s\n",这是格式符。 2.其中的%15,表示显示的宽度为15个字符。 3.默认是右对齐。
  • -: 左对齐
示例: 1.awk -F: '{printf "Username:%-15s\n",$1}' /etc/passwd 分析:左对齐格式。 2.awk -F: '{printf "Username:%-15sid:%5d\n",$1,$3}' /etc/passwd 分析: 1.这是对2个字段进行格式化输出。 2."Username:%-15sid:%5d\n"这是格式符,其中%-15s为第一个字段的格式符,%5d为第二个字段的格式符。
  • +:显示数值的符号
示例: 1.awk -F: '{printf "Username:%+15d\n",$3}' /etc/passwd 分析:  1.%+,表示如果是正数就显示加号,负数就显示负号。  
  •  4、操作符
 
  • 算术操作符:
x+y, x-y, x*y, x/y, x^y, x%y -x:把正数转为负数。 +x: 字符串转换为数值;  
  • 字符串操作符:没有符号的操作符,字符串连接
 
  • 赋值操作符:
=, +=, -=, *=, /=, %=, ^= ++, --  
  • 比较操作符:
>, >=, <, <=, !=, ==  
  • 模式匹配符:
~:是否匹配 !~:是否不匹配  
  • 逻辑操作符:
&&,与 ||,或 !,非  
  • 函数调用:
function_name(argu1, argu2, ...)  
  • 条件表达式:
selector?if-true-expression:if-false-expression   示例:# awk -F: '{$3>=1000?usertype="Common User":usertype="Sysadmin or SysUser";printf "%15s:%-s\n",$1,usertype}' /etc/passwd        
  • 5、PATTERN
功能:地址定界。 (1) empty:空模式,匹配每一行;默认模式。 (2) /regular expression/:仅处理能够被此处的模式匹配到的行;支持基本正则表达式。 示例1:awk '/^UUID/{print $1}' /etc/fstab 分析:1./^UUID/表示以UUID开头的行。 示例2:awk '!/^UUID/{print $1}' /etc/fstab 分析:!/^UUID/中的!表示取反,即非UUID开头的行。 (3) relational expression: 关系表达式;结果有“真”有“假”;结果为“真”才会被处理; 真:结果为非0值,非空字符串;   示例1:awk -F: '$3>=1000{print $1,$3}' /etc/passwd 1.$3>=1000放在program前面,起到了地址定界的作用。限定了只处理id大于1000的行。   示例2:awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd 1.$NF=="/bin/bash"也是起到了地址定界的作用,$NF表示最后一个字段。 2.==是比较操作符,此处可以直接对字符串进行比较。     示例3:awk -F: '$NF~/bash$/{print $1,$NF}' /etc/passwd 1.'$NF~/bash$/,这是地址定界表达式。$NF表示最后一个字段。 2.~用去模式匹配,表示匹配后面的正则表达式,可以匹配则为真。 3./bash$/,正则表达式,$表示行尾锚定。
  • (4) line ranges:行范围,
startline,endline:/pat1/,/pat2/ 指定起始行与结束行,仅处理此范围内的行。 注意: 只能指明模式,不支持直接给出数字的格式 如:awk -F: '/^root/,/^sync/{print $1}' /etc/passwd 1./^root/,/^sync/,这是地址定界表达式。 2./^root/表示起始行的匹配模式。 3./^sync/表示结束行的匹配模式, 4.仅处理在此范围内的行。 示例2:~]# awk -F: '(NR>=2&&NR<=10){print $1}' /etc/passwd 1.(NR>=2&&NR<=10)表示地址定界表达式。NR表示该行的行号。 2.NR>=2,表示起始行,行号大于2 3.NR<=10,表示结束行,行号小于10
  • (5) BEGIN/END模式
  • BEGIN{}: 仅在开始处理文件中的文本之前执行一次;
如: awk -F: 'BEGIN{print "useruid\n------------"}{print $1,$3}' /etc/passwd 分析: 1.此行为类似于打印表头。 2.BEGIN{print "useruid\n------------"}这是第一段program,表示程序开始时运行一次。 2.{print $1,$3},这是第二段program,每读取一行,都执行一次。    
  • END{}:仅在文本处理完成之后执行一次;
示例: awk -F: 'BEGIN{print "user   uid\n------------"}{print $1,$3}END{print "========\nEND"}' /etc/passwd 分析: 1.此行为相当于打印表头,表尾。 2.此处有3段program
  • 6、常用的action
  (1) Expressions,如print等。 (2) Control statements控制语句:if, while等; (3) Compound statements:组合语句; (4) input statements输入语句 (5) output statements输出语句  
  • 7、控制语句
  if(condition) {statments} if(condition) {statments} else {statements} while(conditon) {statments} do {statements} while(condition) for(expr1;expr2;expr3) {statements} break continue delete array[index] delete array exit { statements }  
  • 7.1 if-else
  语法:if(condition) statement [else statement] 基本格式:awk -F <分隔符> '{if (condition) {statement} else {else statement}}}'   ~]# awk -F: '{if($3>=1000) {printf "Common user: %s\n",$1} else {printf "root or Sysuser: %s\n",$1}}' /etc/passwd 分析: 1.if($3>=1000),这是条件判断语句,$3表示第三个字段。如果为真,就执行{printf "Common user: %s\n",$1}。 2.如果为假,就执行{printf "root or Sysuser: %s\n",$1} 3.注意{}的嵌套使用。   ~]# awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd 分析: 1.if($NF=="/bin/bash"),这是条件判断表达式。 $NF,表示当前行的最后一个字段。 ==,表示字符串匹配。 2.如果判断结果为真,就执行print $1   ~]# awk '{if(NF>5) print $0}' /etc/fstab 分析: 1.if(NF>5),这是条件判断表达式,判断字段数量是否大于5. 2.如果为真,就执行print $0。 $0,表示所有字段。   ~]# df -h | awk -F[%] '/^\/dev/{print $1}' | awk '{if($NF>=20) print $1}' 分析: 1.df -h | awk -F[%] '/^\/dev/{print $1}' -F[%],表示以%未分隔符, /^\/dev/,这是地址定界,表示匹配以/dev开头的行。 对符合定界的行,执行{print $1} 2.awk '{if($NF>=20) print $1}' if($NF>=20),这是条件判断,$NF>=20,判断最后一个字段的数值是否大于20. 如果为真,就执行print $1,这里没有明确指明分隔符,就使用默认的空格做分隔符。   使用场景:对awk取得的整行或某个字段做条件判断;  
  • 7.2 while循环
语法:while(condition) statement 条件“真”,进入循环;条件“假”,退出循环; 基本格式:     使用场景:对一行内的多个字段逐一类似处理时使用;对数组中的各元素逐一处理时使用;   ~]# awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {print $i,length($i); i++}}' /etc/grub2.cfg 命令分解: 1.awk '/^[[:space:]]*linux16/ {print $0}' /etc/grub2.cfg 分析:匹配以linux16开头的行,打印出该行。 2.awk '/^[[:space:]]*linux16/ {print $2,length($2)}' /etc/grub2.cfg 分析:匹配以linux16开头的行,打印出该行的第二个字段以及该字段的字符数量。 length($2), 这是awk的内建函数,用于统计字段中的字符数量。 3.awk '/^[[:space:]]*linux16/ {if (length($2)>40) {print $2,length($2)}}' /etc/grub2.cfg 分析:匹配以linux16开头的行,如果该行的第二个字段的字符数量大于40,就打印出来。 加入条件判断语句后,需要嵌套{}。     ~]# awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {if(length($i)>=7) {print $i,length($i)}; i++}}' /etc/grub2.cfg 分析: 1./^[[:space:]]*linux16/,地址定界,匹配以空格加上linux16开头的行。 2.i=1,while(i<=NF),while循环,当i小于字段数量的时候,执行后面的语句。 3.if(length($i)>=7),条件判断语句,当字段的字符数量大于7时。 4.{print $i,length($i)},上述判断结果为真时执行,打印出字段,以及字段的字符数量。 5.i++,自变量加一,打印下一个字段。        
  • 7.3 do-while循环
语法:do statement while(condition) 意义:至少执行一次循环体  
  • 7.4 for循环
语法:for(expr1;expr2;expr3) statement   for(variable assignment;condition;iteration process) {for-body}   使用场景:对文本中的每行的每个字段,逐一遍历,判断是否符合某个条件,并执行相应的处理。   ~]# awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {if(length($i)>=7){print $i,length($i)}}}' /etc/grub2.cfg   特殊用法: 能够遍历数组中的元素; 语法:for(var in array) {for-body} 分析:var会替换array的索引。  
  • 7.5 switch语句
语法:switch(expression) {case VALUE1 or /REGEXP/: statement; case VALUE2 or /REGEXP2/: statement; ...; default: statement}  
  • 7.6 break和continue
break [n] n表示退出第几层循环。 continue  
  • 7.7 next
  提前结束对本行的处理而直接进入下一行;   ~]# awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd 解析:打印出id号为偶数的用户。 $3%2!=0,意思时ID号除2如果余数不等于0,就执行后面的操作,也就是匹配除奇数的id号。 next,跳出本行的处理,直接进入下一行。于是奇数行都会被跳过,只有偶数行会被处理。
  • 8、array
  关联数组:array[index-expression]   index-expression: (1) 可使用任意字符串;字符串要使用双引号; (2) 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”;   若要判断数组中是否存在某元素,要使用"index in array"格式进行;   weekdays[mon]="Monday"   若要遍历数组中的每个元素,要使用for循环; for(var in array) {for-body}   ~]# awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {print weekdays[i]}}' 1.weekdays["mon"]="Monday";weekdays["tue"]="Tuesday,这是定义字典变量weekdays. 2.for(i in weekdays) {print weekdays[i]},这是遍历数组中的索引,然后打印除字典的值。   特殊用法:利用字典变量来计数。 适用场景:用于统计某个文本在字段中重复出现的次数。 state["LISTEN"]++创建字典state,给出字典的key为LISTEN,value进行自增操作。默认value为0,执行一次++,就是0+1=1。 state["ESTABLISHED"]++ 示例: 1.统计tcp处于监听的端口的数量。 ~]# netstat -tan | awk '/^tcp\>/{state[$NF]++}END{for(i in state) { print i,state[i]}}' 解析:统计tcp处于监听的端口的数量。 或者 ss -nl| awk '/^tcp\>/{state[$2]++}END{for(i in state) { print i,state[i]}}' 解析: 1.ss -nl,此命令时,打印当前的协议类型,端口号,以及其状态,结果由空格分隔的5个字段组成。 2./^tcp\>/,这是地址定界,过滤除tcp开头的行。 3.{state[$2]++},这里的意思是,以第二个字段为key,创建字典state,并进行自增操作。 也就是state[LISTEN]++,这个字典的key是LISTEN,value是LISTEN出现的次数。 4.END{for(i in state) { print i,state[i]},END表示在结束阶段执行一次,i表示字典state里面的key,state[i]表示字典的值。也就是打印字典的key以及对应的value。 此处的用意是,打印出LISTEN 及其出现的次数,UNCONNN以及其出现的次数,ESTABLISHED以及其出现的次数。由于此处只有LISTEN一个key,因此仅出现这个。       2.统计 /var/log/httpd/access_log中,每个ip访问资源的次数。 ~]# awk '{ip[$1]++}END{for(i in ip) {print i,ip[i]}}' /var/log/httpd/access_log   练习1:统计/etc/fstab文件中每个文件系统类型出现的次数; ~]# awk '/^UUID/{fs[$3]++}END{for(i in fs) {print i,fs[i]}}' /etc/fstab 1./^UUID/,匹配以UUID开头的行。 2.fs[$3]++,以字典3为key创建数组,并进行自增操作。 3.在结束阶段,打印数组的key,以及对应的value。   练习2:统计指定文件中每个单词出现的次数; ~]# awk '{for(i=1;i<=NF;i++){count[$i]++}}END{for(i in count) {print i,count[i]}}' /etc/fstab 分析: 1.for(i=1;i<=NF;i++),for循环,NF为字段数量,用意是遍历每一行文本中的每个字段。 2.count[$i]++,$i为每一行中的每个字段,以每个字段为key,对其进行统计。 3.for(i in count) {print i,count[i]},打印数组的key,以及对应的value。 4.这是计算文本中的每个字符串在整个文本中出现的次数。  
  • 9、函数
 
  • 9.1 内置函数
数值处理: rand():返回0和1之间一个随机数;   字符串处理: length([s]):返回指定字符串的长度; sub(r,s,[t]):以r表示的模式来查找t所表示的字符中的匹配的内容,并将其第一次出现替换为s所表示的内容; 注意:其返回值,仅仅是否替换成功,成功返回1,不成功返回0. gsub(r,s,[t]):以r表示的模式来查找t所表示的字符中的匹配的内容,并将其所有出现均替换为s所表示的内容;   split(s,a[,r]):以r为分隔符切割字符s,并将切割后的结果保存至a所表示的数组中;   示例:统计每一个客户端IP,对服务器建立了多少连接。 难点: 1.netstat -tan返回的信息中,第4个字段是服务器ip加端口,第5个字段是客户端ip加端口。 2.第5个字段,由客户端iip和端口组成,不同的端口,如果是同一个ip号,可以认为是同一个ip进行的连接,这些连接的数量,就是并发连接数,如果要统计这个数,就需要把ip切割出来,再进行统计。 ~]# netstat -tan | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}END{for (i in count) {print i,count[i]}}' 分析: 1.netstat -tan | awk '/^tcp\>/{{split($5,ip,":")}{print ip[1],ip[2]}}' 1./^tcp\>/,匹配以tcp开头的行,执行后面的操作 {{split($5,ip,":")}{print ip[1],ip[2]}} 2.{split($5,ip,":")},把第五个字段,192.168.60.1:60751,以冒号分隔,分隔后的内容放入字典ip,如ip[1]=192.168.60.1,IP[2]=60751,此处的key是自动生成的。 3.此处经需要统计ip[1]的value。 2.count[ip[1]]++,统计ip[1]的次数。此处是把ip[1]的值192.168.60.1,作为数组count的key,并统计其出现的次数。 3.{for (i in count) {print i,count[i]},打印出数组的key以及值。