一、awk相关知识详解

        1.awk简介

            (1)awk:报告生成器,格式化文本输出

            (2)版本:

                    New awk(nawk)

                    GNU awk(gawk)


        2.基本用法

                awk [option] 'program' var=value file...

                awk [option] -f programfile var=value file...

                awk [option] 'BEGIN{action;...};pattern{action...};END{action...}' file...

                说明:

                    awk程序通常由:BEGIN语句块,能够使用模式匹配的通用语句块,END语句组成

                    program通常是被单引号引住


                    option:

                        -F 指明输入时用到的分隔符,默认为空格(连续空格当做一个空格)

                        -v var=value 自定义变量

        3. awk工作原理

                (1)分割符、域、记录相关概念

                         <1>awk执行时,由分隔符分割的字段(域)标记$1,$2...成为域标识,$0为所有域;和shell中的变量$符含义不同

                        <2>文件的每一行为一个记录

                (2)awk工作原理

                            第一步:执行BEGIN(action)语块中的语句

                            第二步:从文件或标准输入读取一行,然后执行pattern{action;...}语句块,它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取

                            第三步:当读至输入流末尾时,执行END(action...)语句块


                        说明:                        

BEGIN语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中,不可打印多个文件的行参数;只输出一行;


END语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块


pattern语句块中的通用命令是最重要的部分,也是可选的。如果没有提供pattern语句块,则默认执行(print $0),即打印每一个读取到的行

   

        4.awk基本格式

            awk [option] 'program' file...

                    program:pattern{action statements;...}

                    pattern和action

                            pattern语句决定动作语句何时触发(BEGIN END)

                            action statements 对数据进行处理,放在{}指明,(print,printf)

            说明:同一个pattern语句块的action之间用“;”隔离,print输出的不同域之间用“,”隔离;省略action,默认执行print $0操作

        5.print格式说明

            print item1 ,item2....

       注意:逗号分隔符

                输出的各item可以字符串,也可以是数值;当前记录的字段、变量或awk的表达式

                如省略item,相当于print $0

                输出内容除了变量,用双引号括起来


举例:

<1> 以“:”为分隔符,显示/etc/passwd每一行信息

awk –F: '{print}' /etc/passwd

<2>以“:”为分隔符,把/etc/passwd每一行以wang信息代替

awk –F: ‘{print “wang”}’ /etc/passwd

<3>以“:”为分割符,显示该文件每一行的第一个字段

awk –F: ‘{print $1}’ /etc/passwd

<4>在标准输入的每一行输出“hello,world”

wKioL1fkkfbAW4l-AAAPocI-Axg817.png

            6.printf命令详解   

                <1>格式化输出:printf "FORMAT" ,item1,item2,...

                            必须指定FORMAT

                            不会自动换行,需要显式给出换行控制符,\n

                            PARMAT需要分别为后面每个item指定格式符

                <2>格式符:与每个item一 一对应

                        %c:显示字符的ASCII编码

                        %i:显示十进制整数

                        %e:显示科学计数法数值

                        %f:显示为浮点数

                        %g:以科学计数法或浮点形式显示数值

                        %s:显示字符串

                        %u:无符号整数

                        %%:%本身

                <3>修饰符

                        #[.#]:第一个数字控制显示的宽度,第二个表示小数点后的精度

                        -:左对齐(默认是右对齐)

                        +:显示数值的正负符号

                <4>实例

                        awk -F : '{printf "%s",$1}' /etc/passwd

                        awk -F: ‘{printf "Username: %-15s, UID:%d\n",$1,$3}’ /etc/passwd

                疑问:在printf格式化输出中,awk内置变量还有效吗?

wKioL1fknOPSLD_iAAAdGRcspM0557.png

wKiom1fknMrx_BjnAAAReP9Drpc531.png

            发现OFS变量并没有生效,在printf语句中,格式化输出在format设置就好

            7.awk变量(引用的时候不用加$符号)

            (1)内置变量

                <1>FS(field separator):输入字段分割符,默认为空格

                        awk -F : '{print $1,$3}' /etc/passwd

                        awk -v FS=":"  '{print $1,$3}' /etc/passwd

                <2>OFS输出字段分隔符,默认为空格;

                        OFS默认为空格:此时输出字段间是以空格为分隔符

wKiom1fklcbypYgwAAAUalzIVQk541.png

                        OFS为“+++”:

wKioL1fklebxvuSXAAAKtd1XELg212.png

                <3>RS(record separator)记录输入时分隔符,原换行符仍旧有效

                        新建文件内容如下

wKioL1fkljLAEqLDAAAEZmwXtps480.png

                        RS为“ ”:此时c d为一行,只不过c后边的原换行符依旧有效,故看起来不在一行

wKiom1fklk3gI2yvAAAQ8O-sss8252.png

                        为更深刻的理解RS,看下边两图


wKioL1fkmD7TNnH_AAAOf398Y64448.png

wKioL1fkmE-wTmYhAAAPajhr9FM578.png

                    说明:因为awk输出字段分割符默认是空格,原来的换行符会被覆盖


                <4>ORS:输出记录分割符,用指定符号代替换行符(此时原来的换行符还是有效的)

                         新建文件内容如下:

wKiom1fkmaSjVuPyAAACumRMdcI002.png

                         ORS为“+++”:

wKiom1fkmerwC88TAAAJpq2HpWk460.png

                        说明:ORS只是把空格转换为“+++”,原先的换行符并没有转换,因为c和e、h后边是由换行符的,但是并没有转换

                <5>NF字段数量

                        awk -F:‘{print NF}’ /etc/fstab

                        awk -F: '{print $(NF-1)}' /etc/passwd  显示倒数第二个字段

                <6>行号 NR

                        awk '{print NR}' /etc/fstab

                        awk END'{print NR}' /etc/fstab

                <7>FNR 各文件分别统计行号

                        awk '{print FNR}' test test1

                <8>FILENAME 当前文件名

                         awk '{print FILENAME}' test

                <9>ARGC 命令行参数的个数

                        awk BEGIN'{print ARGC }' test test1

                        awk '{print ARGC }' test test1

                <10>ARGV :数组,保存的是命令行给定的各参数(索引从0开始)

                        awk ‘BEGIN {print ARGV[0]}’ /etc/fstab /etc/inittab

                        awk ‘BEGIN {print ARGV[1]}’ /etc/fstab /etc/inittab

wKiom1fkmsaSHQFcAAApEOr9jus844.png

        (2)自定义变量:

                <1>在program中直接定义

                <2>-v var=value   变量名区分大小写

                        awk-v test='hello gawk' '{print test}' /etc/fstab

                        awk-v test='hello gawk' 'BEGIN{print test}'

                        awk'BEGIN{test="hello,gawk";print test}'

        8.操作符

                <1>算术操作符

                        x+y , x-y , x*y , x/y , x^y , x%y

                        -x:转换为负数

                        +x:转换为整数


                    awk -v x=1 -v y=2 'BEGIN{print x+y}'

                <2>赋值操作符

                        =, +=, -=, *=, /=, %=, ^=

                        ++, --

                            awk -v a=1 -v b=2 'BEGIN{print b+=a}'

                <3>比较操作符

                        >, >=, <, <=, !=, ==


                <4>模式匹配符:

                        ~:左边是否和右边匹配包含  模式匹配的话,被匹配模式需要被/ /括起来

                        !~:是否不匹配

                            cat /etc/passwd|awk'$0 ~ /root/'|wc-l

                            cat /etc/passwd|awk'$0 !~ /root/'|wc-l

                 <5>逻辑操作符:&& || !

                        awk –F: '$3>=0 && $3<=1000 {print $1}' /etc/passwd

                        awk -F: '$3 ==0 || $3>=1000 {print $1}' /etc/passwd

                        awk -F: ‘!($3==0){print $1}' /etc/passwd

                        awk -F: '!($3>=500) {print $3}}' /etc/passwd

                   <6>条件表达式

                        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

            9.pattern:根据pattern条件,过滤匹配的行,再做处理

                    <1>如果未指定:空模式,匹配每一行

                    <2>/regular expression/:仅处理能够匹配到的行

                                awk'/^UUID/{print $1}' /etc/fstab

                                awk'!/^UUID/{print $1}' /etc/fstab

                    <3>relation expression:关系表达式;结果为真才会处理

                                真:结果非0,非空

                                假:结果为空字符或0


                    实例:

                            awk -F: '$3<1000{print $1,$3}' /etc/passwd

                            awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd

                            seq 10 | awk 'i=!i'  显示奇数行;显示偶数行的话,提前给i赋值为1即可

wKiom1fkv_LwWweZAAAI0dAfxpU447.png

wKiom1fkv_KRzYAuAAAKP9UGKS8935.png

                            awk -F: '""{print}' test 啥都不输出

                            awk -F: 'i=1;j=1{print $1,$3}' test

wKioL1fkwCuCUnR-AAATfHEFA7U076.png

                    <4>line ranges(行范围)

                        ”startline,endline“:/pat1/,/pat2/不支持直接给出数字格式

                            效果一样:

                                     awk-F: '(NR>=2&&NR<=10){print $1}' /etc/passwd

                                      awk-F: 'NR>=2&&NR<=10{print $1}' /etc/passwd


                        awk-F: '/^root/,/^nobody/{print $1}' /etc/passwd

                    <5> BEGIN/END模式

                        BEGIN{}: 仅在开始处理文件中的文本之前执行一次

                        END{}:仅在文本处理完成之后执行一次

            10.常用的action

                    <1>Expression:算术,比较表达式

                    <2>Control Statements :if while 等

                    <3>compound statements:组合语句

                    <4>input statements

                    <5>output statements:print等

            11.awk控制语句if-else


                if (condition) statement [else statement]

                if (condition1) {statement1} else if (condition2) {statement2} else {statement3}

        说明:(如果statement为多条命令,必须加“{}”,一条的话,可以不加),一般情况下还是加上大括号比较好


                实例:

                        awk BEGIN'{test=65;if(test>80) {print "it is good"} else if(test<60) {print "you are not ok"} else {print "you are right"}}'

                        awk-F: '{if($3>=1000)print $1,$3}' /etc/passwd

                        df-h|awk-F[%] '/^\/dev/{print $1}'|awk'{if($NF>=80) print $1}‘

            12.awk控制语句while

                    while(condition)statement,条件真,进入循环;条件假,退出循环

                        使用场景:对一行内的多个字段逐一类似处理的时候

                                        对数组中的各元素逐一处理时使用


                    实例:awk'/^[[:space:]]*linux16/{i=1;while(i<=NF) {print $i,length($i); i++}}' /etc/grub2.cfg

wKioL1fkwsfwlgNvAAAYYilm7N8948.png

wKiom1fkwteRPQNSAAAmvHawUQk272.png

                            awk'/^[[:space:]]*linux16/{i=1;while(i<=NF) {if(length($i)>=7) {print $i,length($i)}; i++}}' /etc/grub2.cfg

            13.awk控制语句do-while

                    do statement while(condition) 无论真假,至少执行一次


                        实例: awk 'BEGIN{total=0;i=0;do{total+=i;i++}while(i<=100);print total}'

wKiom1fkwx3BfzpZAAAOQLBtbog665.png

            14.awk控制语句-for循环

                for循环

                语法:for(expr1;expr2;expr3) statement

                          for(variable assignment;condition;iterationprocess) {for-body}

                特殊用法:能够遍历数组中的元素;

                        语法:for(varin array) {for-body}

                示例:

                        awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg

wKiom1fkw5bBh5LmAAArWreZxm4200.png

            15.awk控制语句switch

                语法:switch(expression) {case VALUE1 or /REGEXP/: statement; case VALUE2 or /REGEXP2/: statement; ...; default: statement}

            16.awk控制语句break、continue、next

                    <1>awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i%2==0){continue}{sum+=i}};print sum}'

wKiom1fkxZzhg9WoAAAK6t0qB1Y398.png

                            continue [n]

                    <2>awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i==11){break}{sum+=i}};print sum}'

wKioL1fkxaihwQMHAAAJv3OuW-A387.png

                            break [n]

                    <3>next  提前结束对本行处理而直接进入下一行处理(awk自身循环)

                            awk-F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd

            17.awk数组

                <1>array["index-expression"]

                        index-expression:

                                可使用任何字符串,但字符串需要用双引号括起来(变量的话,就不用加了)

                                如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”

                                若要判断数组中是否存在元素,要使用“index in array”格式进行遍历

                    实例一:

                    awk 'BEGIN{week["mon"]="monday";week["tue"]="tuesday";print week["mon"]}'

                    awk '!a[$0]++' test2


wKiom1fkxeqyOEWxAAAjaPiaV2M616.png

                    说明:命令三显示的是test2文件的内容,命令三是为了更好地理解“$0”与“arr[$0]”之间的关系,命令一是去掉test2中重复的行;

 对命令一来说:应该把“arr[$0]“当做一个整体,例如当做“i”,此处用到i++和++i的相关知            识;arr[aa]=0-->!arr[aa]=1-->arr[aa]=1;arr[bb],arr[cc],arr[dd]类似;当再次到arr[aa]时,arr[aa]=1-->!arr[aa]++=0-->arr[aa]=2;如果第三次遇到arr[aa]         时,arr[aa]=2-->!arr[aa]++=0-->arr[aa]=3;

                    实例二:统计开放的tcp类型的端口的访问状态

wKiom1fkxhuAvrnPAAALs_sq_Zo534.png

                <2>i++和++i的区别

                        awk 'BEGIN{i=0;print i++,i}'

                        awk 'BEGIN{i=0;print ++i,i}'

wKiom1fkxifj74d5AAAJVycPi8g321.png

            18.awk函数

                (1)系统函数

                      <1>rand():返回0和1之间的一个随机数

                            实例:

                                    awk'BEGIN{srand(); for (i=1;i<=10;i++)print int(rand()*100) }'

执行awk 'BEGIN{print rand()}'

wKioL1fkxnThe5kOAAAPdQ4MbI4265.png

                                发现随机数一直一样

                                执行awk 'BEGIN{srand();print rand()}'

wKiom1fkxoOBdh9TAAAP-088spw167.png

                   <2>length([s]):返回指定字符串的长度,里边的s是变量

                            实例:awk ’BEGIN{a="tian";print length(a)} ‘

wKiom1fkxyvjK-3IAAANfHYu3oc774.png

                    <3>sub(r,s,[t]):对t字符进行搜索r表示的模式匹配的内容,并将第一个匹配到的内容替换为s

                    <4>gsub(r,s,[t]):对t字符进行搜索r表示的模式匹配的内容,并将全部内容替换为s

实例:

wKioL1fkx0yjq0UVAAAN6nchCrI358.png

                    <5>split(s,array(),[r]):以r为分隔符切割字符s,并将切割后的交过保存至array所表示的数组中(第一个索引值是1,往后依次累加)

                        实例:netstat-tan | awk'/^tcp\>/{split($5,ip,":");count[ip[1]]++}END{for (i in count) {print i,count[i]}}'

             

wKioL1fkytbBqrh2AAAxMShmTkU269.png

wKiom1fkytaggttaAAAW8Gx6n8A091.png

wKioL1fkytbCN9GtAAAOhDaIOT0745.png

        (2).自定义函数

                格式:function name (parameter1,parameter2...){

                                statements

                                return expression

                            }

                实例:

                        方法一:#cat fun.awk

                                        function max(v1,v2) {

                                                v1>v2?var=v1:var=v2

                                                return var

                                        }

                                        END{a=3;b=2;print max(a,b);}

                                        #awk –f fun.awktestfile

                        方法二: #cat fun.awk

                                        function max(v1,v2) {

                                                v1>v2?var=v1:var=v2

                                                return var

                                        }

                                        END{print max(3,2)}

                                        #awk –f fun.awktestfile

                        方法三:#cat fun.awk

                                     #!/bin/awk -f

                                     function max(v1,v2) {

                                            v1>v2?var=v1:var=v2

                                            return var

                                     }

                                    END{a=3;b=2;print max(a,b);}

                                    #chmod u+x fun.awk

                                    #fun.awk

            19.awk中调用shell命令

                system(命令)用来在awk命令中调用shell命令;(这是一个函数)

                空格是awk中的字符串连接符,如果system中需要使用awk中的变量可以使用空格分隔,


                    awk BEGIN'{system("hostname") }'

                    awk'BEGIN{a=12; system("echo  " a) }'带颜色部分是用来分离shell命令和awk变量的

wKioL1fkzQOyvLYbAAAPoLWfcBU436.png

            20.awk脚本,将awk程序写成脚本,直接调用或执行

                        直接调用

wKioL1fkzTej2Zl5AAAkRO-2D-I734.png

                        直接执行

            21.向awk脚本传递参数

                    格式:awk.awk var=value var1=value1... inputfile

                            实例一:像脚本中传递参数

wKioL1fkzX_h47LEAAAjVDYy0eQ397.png

spacer.gif

                            实例二:向直接执行的脚本中的函数传递参数

wKioL1fkzYyj2eDDAAAaU8mlxsQ061.png

spacer.gif

                                    小结:发现像实例一  一样直接给函数传递参数是不行的,必须以变量的形式传递参数

二、课后作业

1、统计/etc/fstab文件中每个文件系统类型出现的次数

    

    (1)查看/etc/fstab文件内容

wKioL1fk0GSCyOiXAAAaa0SnMXc560.png

    (2)执行如下命令

wKiom1fk0KjRAV0OAAAHqqSyjZI207.png

2、统计/etc/fstab文件中每个单词出现的次数;
    (1)查看/etc/fstab文件内容

wKiom1fk0QHAYzi6AAAZngH1sqs356.png

    (2)执行如下命令

wKioL1fk1RmQt1u-AAAcGupHoBA681.png

3.新建如下文件,分别统计1班和2班的平均分

wKiom1fk1vni9D9rAAAEI-alkX0055.png

    输入如下命令

wKiom1fk3s6TnAHRAAAWY_bt8fw093.png

发现第二行的命令是正确的,因为第一行的命令没读取一行数据,s1和s2被重新赋值一次