1、linux文本处理工具:

    文本过滤工具:grep、egrep、fgrep

    行编辑工具:sed

    格式化文本工具:awk、gawk

注意:centos7中,awk是指向gwak的链接文件。

[root@localhost ~]# ll -l `which awk`

lrwxrwxrwx. 1 root root 4 Aug 29 20:01 /usr/bin/awk -> gawk

[root@localhost ~]# 


2、awk工作原理:

    awk从文本中一次读取一行,按指定的分隔符切割成若干个片段部分,默认的分隔符是“空白”字符,每个分片都保存在awk内建的变量中($1,$2,$3...),然后用动作(常用的print、printf命令)去输出这些行中的某一个分片、某几个或者整行分片。整行内容用"$0"表示,切割出来的每一分片,也可以进行加工处理。



    

3、awk命令:

   格式:gawk [OPTIONS] 'program' FILE ...   //注意program要用单引号引起来

3.1、awk的OPTIONS:

    -F:指定输入时的字段分割符,用于文件中读取时以什么为分割符;默认为空格分割符

    -v var=value :自定义变量



注意:awk是文本格式化工具,但也是一种完整的编程语言,因此他向其他编程语言一样,也包含:变量(3.3节)、操作符(3.4节)、控制语句(3.5节)、数组(3.6节)、函数(3.7节),这些都可以在'program'中使用


3.2、awk的program的格式:

    PATTERN{ACTION STATEMENT}  //此处的PATTERN作用主要是界定需要编辑的行,action执行的动作,statement要执行的语句,可以有多个,语句之间以分号(;)分隔。

示例:

[root@localhost ~]# tail -3 /etc/fstab | awk '{print $2,$4}'  //输出时默认以空白字符分隔片段;cut只能理解单个或固定的空白字符。


PATTERN:

    ①、空模式:empty,不定义pattern,表示处理文件中的每一行,即匹配每一行,

    ②、/regular expression/:仅处理能够被此处模式匹配到的行,两个斜线(/)必须要写

          示例:[root@localhost ~]# awk '/^UUID/{print}' /etc/fstab   //输出以UUID开头的行

            [root@localhost ~]# awk '!/^UUID/{print}' /etc/fstab  //输出不以UUID开头的行

    ③、relation expression关系表达式(也叫比较表达式),结果有真有假,结果为真的才会被处理,结果为假的行不会被处理;真:结果非0值,假:结果为0值

           示例:[root@localhost ~]# awk -F: '$3>=1000{print $1,$3}' /etc/passwd  //打印每行UID大于1000的第一和第三个分片字段。

              [root@localhost ~]# awk -F: '$NF=="/bin/bash"{print $1,$7}' /etc/passwd   //匹配最后一个分片是/bin/bash的行,显示第一和第七个分片字段

              [root@localhost ~]# awk -F: '$NF~"bash"{print $1,$7}' /etc/passwd    //通配最后一个分片是/bin/bash的行,显示第一和第七个分片字段

    ④、line ranges行范围,界定从满足第一个模式到下一个模式之间的行 

         格式:/pat1/,/pat2/              

         示例: [root@localhost ~]# awk -F: '/^root/,/^m/{print $0}' /etc/passwd   //界定以root开头到以m结尾的用户,然后输出匹配项的整行内容

            [root@localhost ~]# awk -F: '(NR>=2 && NR<=10){print $0}' /etc/passwd  //显示行号从2到10这些行的整行内容

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

       END{ }   //仅在文本处理之后,命令结束之前执行一次

        示例:[root@localhost ~]#  head -10 /etc/passwd | awk -F: 'BEGIN{print "username   uid\n============"}{print $1,$3}END{print "===end"}' 

    





常用的action:

    ①、expression:运算表达式   

    ②、control statement:控制语句,如if、while语句等

    ③、compound statement:组合语句

    ④、input statement:输入语句

    ⑤、outprint statement:输出语句,如print,printf


⑤.1、输出语句--print

    语法格式:print item1,item2,...  

     解释:多个字段(item)以逗号分隔,否则输出时是合并在一起输出的;

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

        如果省略item,则输出整行内容,相当于打印"$0"

print示例:

[root@localhost ~]# tail -3 /etc/fstab  | awk '{print "hello:" $1,$2}'  

hello:UUID=43446f03-00c0-4d49-a4f3-2050cecfff9e /boot

hello:/dev/mapper/cl-swap swap

[root@localhost ~]# tail -3 /etc/fstab  | awk '{print "hello: $1,$2"}'  //print后放在双引号中的内容不会被替换成其他数值。

hello:/dev/mapper/cl-root /

hello: $1,$2

hello: $1,$2

hello: $1,$2

[root@localhost ~]# 


⑤.2、输出语句--printf:格式化输出

    语法格式:printf FORMAT,item1,item2,...   //format为格式符,它会为每个item占一个位置,item的数值会套用format中定义的格式。

    语法要点:

        FORMAT必须给出

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

         FORMAT中需要分别为后面的每个item指定一个格式化符号


FORMAT格式符介绍:

    %c:显式字符的ASCII码

    %d或%i:显式为十进制的整数

    %e或%E:科学计数法数值显式

    %f:显式为浮点数

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

    %s:显示为字符串

    %u:显示为无符号整数

    %%:显示%自身

    

printf示例:

[root@localhost ~]# awk -F: '{printf "username:%s\n",$1}' /etc/passwd  //打印出passwd中第一个片段并套用%s格式。

[root@localhost ~]# awk -F: '{printf "username:%s,uid:%d\n",$1,$3}' /etc/passwd    //除了格式符,其他放在双括号中的字符原样输出。


FORMAT格式符的修饰符:用来控制格式符的显示机制,如对齐;放在格式符前,如:%[修饰符]s

 修饰符:

    #[.#] :[.#]可以省略,不是通配符的意思,第一个数字"#"表示控制显示的宽度,第二个数字"#"表示小数点的精度,如果没有小数,可以省略。

      示例:%3.1f

      [root@localhost ~]# awk -F: '{printf "username:%10s,uid:%10d\n",$1,$3}' /etc/passwd  //默认右对齐

    -:左对齐,默认为右对齐  

      示例:[root@localhost ~]# awk -F: '{printf "username:%-10s,uid:%10d\n",$1,$3}' /etc/passwd

    +:显示数值的符号,正数为“+”,负数为“-”

      示例:[root@localhost ~]# awk -F: '{printf "username:%-10s,uid:%+10d\n",$1,$3}' /etc/passwd




3.3、awk的变量介绍:

 内建变量:

        FS:input filed Seperate,输入片段分隔符,默认为空白字符

        OFS:output filed seperate,输出片段分隔符,默认为空白字符

           示例:[root@localhost ~]# awk -v FS=':' '{print $1,$2}' /etc/passwd //指定输入分隔符为: ,然后打印出每行的第一个和第二个分片部分 

              [root@localhost ~]# awk -F: '{print $1}' /etc/passwd   //直接用—F选项指定分隔符:

              [root@localhost ~]# awk -v FS=':' -v OFS=':' '{print $1,$2}' /etc/passwd   //指定输入分隔符和输出分隔符,每个-v选项定义一个变量

        RS:input record Seperate:指定输入时的换行符,很少用到

        ORS:outpunt reccord seperate:指定输出时的换行符,很少用到

          示例:[root@localhost ~]# head -1 /etc/passwd | awk -v RS=':' '{print $0}'   //指定输入时的换行符为:

              [root@localhost ~]# head -1 /etc/passwd | awk -v RS=':' -v ORS='#'  '{print $0}'  //指定输入时的换行符为:,输出换行符#             

        NF:number of filed,统计每一行分片后的分片个数

          示例:[root@localhost ~]# head -1 /etc/passwd | awk  -v FS=':' '{print NF}' //统计以':'分隔后分片字段个数,结果为7,注意此处的NF不要加$符号

              [root@localhost ~]# head -1 /etc/passwd | awk  -v FS=':' '{print $NF}' //如果NF前加上$符号,表示引用每行的最后一个分片字段,相当于$7;

                注意:awk中引用变量时不要加$符号,但是引用分片字段时要加$符号

        NR:number of reord:统计行数(会显示每一行的行号,如果有多个文件会统一编号)

          示例:[root@localhost ~]# awk '{print NR,$1}' /etc/fstab  //统计文件有多少行

              [root@localhost ~]# awk '{print NR,$1}' /etc/fstab /etc/issue  //有多个文件时会统一编号

        FNR:number of reord:统计行数(会显示每一行的行号,如果有多个文件不会统一编号)

          示例:[root@localhost ~]# awk '{print FNR,$1}' /etc/fstab /etc/issue  //有多个文件时不会统一编号

        FILENAME:显示被处理的文件名称,即被awk处理的文件的名称

          示例:[root@localhost ~]# awk '{print FILENAME}' /etc/issue  //会每行显示一个文件名

        ARGC:bash命令行参数的个数

        ARGV:数组,保存的是bash命令行所给定的各参数

         示例:[root@localhost ~]# awk 'BEGIN{print ARGC}' /etc/issue   //ARGC统计命令行给的参数个数

               2

               [root@localhost ~]# awk 'BEGIN{print ARGV[0]}' /etc/issue  //引用bash命令行保存的数组参数

               awk

               [root@localhost ~]# awk 'BEGIN{print ARGV[1]}' /etc/issue  

               /etc/issue

               [root@localhost ~]# awk 'BEGIN{print ARGV[2]}' /etc/issue  


自定义变量:

    方法一:-v var=value  //var为变量名,value是变量值;需要注意的是:awk中的变量名区分大小写

          示例:

            [root@localhost ~]# awk -v test='hello magedu' '{print test}' /etc/issue  //print后此处引用变量不要加$符号

            hello magedu

            hello magedu

            hello magedu

    方法二:在program中直接定义:

         示例:

            [root@localhost ~]# awk  '{test="hello magedu";print test}' /etc/issue  //多个语句间用分号分隔。

            hello magedu

            hello magedu

            hello magedu

            [root@localhost ~]# 

   

3.4、awk操作符介绍:

    ①、算术运算操作符:

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

        解释 -x:把正数变成负数;

           +x:把一个字符串转成数值

    ②、字符串操作:没有符号的操作符表示字符串连接

    ③、赋值操作符:=、+=、-+、*=、/=、%=、^=、++、--

    ④、比较运算符:>、>=、<、<=、!=、==    //注意一个=是变量赋值,两个==是做比较运算

    ⑤、模式匹配符:

        ~ :是否匹配;左侧的字符串是否能被右侧的模式所匹配

         !~:是否不匹配,左侧的字符串是否不能被右侧的字符串所匹配

    ⑥、逻辑操作符:

        &&:与

        ||:或

        !:非

    ⑦、函数调用操作:

        function_name(argu1,argu2,...)  //和bash的函数调用不同,这里要带小括号,括号中可以有也可以没有参数

    ⑧、条件表达式:

        selector?if-true-expression:if-false-expressiom  //selector是一个条件表达式,结果又真和假之分,如果为真则执行if-true-expressiom,否则,执行假的语句。注意:真假语句之间用冒号(:)分隔。

        示例:判断一个用户是系统用户还是普通用户(centos7)

          [root@localhost ~]# awk -F:  '{$3>=1000?usertype="commuser":usertype="systemuser";printf "%15s:%-s\n",$1,usertype}' /etc/passwd /

 

3.5、awk控制语句介绍:

①、if(condition) {statement}  //单分支if语句,如果statement只是一条语句,可以省略“{ }”

②、if(condition) {statements} else {statement}   //双分支if语句,语句部分必须带“{ }” 

        if语句使用场景:对awk取得的整行或某个分片字段做出条件判断

        示例:[root@localhost ~]# awk -F: '{if($3>=2000){print $1,$3}}' /etc/passwd   //显示出uid大于2000的用户和id

            [root@localhost ~]# awk -F: '{if($3>=1000){print "comm user:" $1,"uid:"$3} else {print "sysuser:" $1,"uid:"$3}}' /etc/passwd  //判断用户类型

            [root@localhost ~]# awk -F: '(NR>=2 && NR<=10){if($NF!="/bin/bash") {print $1,$7}}' /etc/passwd  //显示第二行到第十行,默认shell不是bash的用户

           [root@localhost ~]# awk  '{if(NF>=5) {print $0}}' /etc/fstab    //显示分片后字段数大于5的行

            

           [root@localhost ~]# df -h   //显示文件系统的空间使用情况

            [root@localhost ~]# df -h | awk -F% '{print $1}'

             [root@localhost ~]# df -h | awk -F% '{print $1}' | awk '{if($NF>=50) {print $1,$NF}}'  //显示空间使用大于50%的那些文件系统


③、while(condition) {statement}  //当条件为真时才执行语句;注意:没必要使用while语句去遍历文件中的每一行,因为awk内置的功能就会完成遍历操作

        while语句使用场景:

            对一行内的多个字段逐一进行类似处理的操作,使用while语句

            对数组中的各元素逐一处理操作时,使用while语句

        

        示例:显示文本中每个满足条件的行,统计行内字段的个数和每个字段中的字符个数。

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

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


④、do {statement} while(condition)  //无论条件为真或假,都先执行一次循环体

⑤、for(exp1;exp2;exp3) {statement}   //exp1:变量初始化;exp2:条件判断;exp3:变量修正


          示例:显示文本中每个满足条件的行,统计行内字段的个数和每个字段中的字符个数。

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


   for循环特殊用法:能够遍历数组中的元素

          语法格式:for(var in array) {for-body} //var是变量,in是关键字,array是数组名;意思是让var遍历array的索引(下标)。


⑥、switch语句:多分支的if语句,类似bash脚本中的case语句。

    语法格式:swith(expression) {case value1:statement1;case value2:statement2;....;default:statement}

    注意:expression是表达式,case是关键字,value可以是一个“值”也可以是“模式”,用来和expression做比较,如果哪个value的值与expression的值相匹配,执行其后的语句,都不匹配则执行default后的语句


⑦、break [n]  //跳出n层循环语句

  continue    //提前结束本轮循环,进入下一轮循环,在awk中主要用来控制行内循环

  

   next      //用来控制awk的内生循环;作用是提前结束本行处理,进入下一行文本处理

      

       next示例:显示用户id为偶数的用户

       [root@localhost ~]# awk -F: '{if($3%2!=0){next};print $1,$3}' /etc/passwd


⑧、delete array[index]   //删除指定数组元素

   delete array       //删除整个数组

⑨、exit      //退出语句

⑩、{statement}

     

3.6、awk中的数组(arrary介绍):

awk支持的数组类型:

    常规数组:下标为数字的数组

    关联数组:下标为字符的数组,在awk中,此种数组用的比较多   


数组定义:array[index-expression]

  index-pression的表示方式:

    ①、可以使用任意字符串,字符串使用双引号(" ")引起,单引号用来标识program 

    ②、如果某数组元素实现不存在,在引用时,awk会自动创建此元素索引,并将其值初始化为“空字符串”,因此要判断数组中是否存在某元素,需要使用 for(var in array) {for-body}格式进行。

示例:

[root@localhost ~]# awk 'BEGIN{weekdays["mon"]="monday";weekdays["tue"]="tuesday";print weekdays["tue"]}' 

[root@localhost ~]# awk 'BEGIN{weekdays["mon"]="monday";weekdays["tue"]="tuesday";for(i in weekdays) {print weekdays[i]}}'   //遍历数组中的元素,var会遍历array中的每个索引

 

[root@localhost ~]# netstat -tan

Active Internet connections (servers and established)

Proto Recv-Q Send-Q Local Address           Foreign Address         State      

tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN     

tcp        0      0 192.168.122.1:53        0.0.0.0:*               LISTEN     

tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN     

tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN     

tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN     

tcp6       0      0 :::111                  :::*                    LISTEN     

tcp6       0      0 :::22                   :::*                    LISTEN     

tcp6       0      0 ::1:631                 :::*                    LISTEN     

tcp6       0      0 ::1:25                  :::*                    LISTEN     

用数组统计上述netstat -tan命令中状态出现的次数:

[root@localhost ~]# netstat -tan | awk '/^tcp\>/{state[$NF]++}END{for(i in state){print i,state[i]}}'

LISTEN 5


统计/var/log/httpd/access_log-20180114日志文件中,访问的ip出现的次数

[root@localhost httpd]#  awk '{ip[$1]++}END{for(i in ip) {print i,ip[i]}}' /var/log/httpd/access_log 


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

[root@localhost httpd]# awk '/^UUID/{fs[$3]++}END{for(i in fs){print i,fs[i]}}' /etc/fstab 

xfs 1

[root@localhost httpd]# 


统计指定文件中每个单词出现的次数

[root@localhost httpd]# awk '{for(i=1;i<=NF;i++){count[$i]++}}END{for(i in count){print i,count[i]}}' /etc/fstab 


3.7、awk中的函数:

   内置函数

   自定义函数


   内置函数:

    ①、rand():返回0和1之间的一个随机数(小数)

        示例:  

            [root@localhost httpd]# awk 'BEGIN{print rand()}'   //取出的随机数如果不做处理,那么会一直保持此值不变

            0.237788

            [root@localhost httpd]# 


    ②、字符串处理函数      

        length([s]) :返回指定字符串[s]的长度,不过不写[s]返回“0值”

        sub(r,s,[t]):以r表示的模式来查找t所表示的字符串中的匹配的内容,并将其第一次出现替换为s所表示的内容;t可以省略,替换后返回的是“1”或“0”

            示例:[root@localhost httpd]# awk 'BEGIN{print sub(wo,ni,nilaizhaowohaobu)}'

               [root@localhost httpd]# awk '{print sub(o,O,$1)}' /etc/passwd

        gsub(r,s,[t]):以r表示的模式来查找t所表示的字符串中的匹配的内容,并将其全部出现替换为s所表示的内容;t可以省略,替换后返回的是“1”或“0”

        split(s,a[,r]):以r为分隔符切割字符s,并将切割后的结果保存至a所表示的数组中,a是一个数组,从表从“1”开始,1,2,3....

            示例:[root@localhost httpd]# netstat -tan | awk '/tcp\>/{split($5,ip,":");count[ip[i]]++}END{for(i in count){print i,count[i]}}'