文本处理三剑客:

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

         sed:称为流编辑器,行编辑器

         awk:称为报告生成器,主要用于格式化文本输出

        

        awk:Aho,Weinberher,Kernighan 

              由这个三个作者创建而成,最初是用在UXIN系统上面,后来被GNU组织不断对其进行更新,就形成了gawk,现在linux上用的都是gawk。         

[root@localhost tmp]# which awk
/bin/awk
[root@localhost tmp]# ll -d /bin/awk 
lrwxrwxrwx. 1 root root 4 Mar 11 17:28 /bin/awk -> gawk   //awk就是gawk的链接文件

    awk的基本用法:       

             gawk [options] 'program' FILE ...

                     program: PAT{aciton STATEMENS}

                          STATEMENS(语句): 多个语句之间用分号分隔

       1、常用选项:

                         -F:指明输入输出时用到的分隔符,默认是空白字符

                         -v var=varlue:自定义变量

                awk中的变量:

                         1):内建变量

                               FS:表示输入时的分隔符;默认是空白字符             

[root@localhost tmp]# awk -v FS=' ' '{print $1}' /etc/passwd   //以空白作为分隔符
root:x:0:0:root:/root:/bin/bash
ftp:x:14:50:FTP              //后面还有User:/var/ftp:/sbin/nologin

                              OFS:表示输出时的分隔符;默认为空白字符

[root@localhost tmp]# awk -v FS=':' -v OFS=':' '{print $1}' /etc/passwd
root
bin

                              RS:表示输入的行分隔符,意思就是换行符

[root@localhost tmp]# awk -v RS=':' '{print}' /etc/passwd
root
x
0
0
root
/root
/bin/bash

                            ORS:表示输出的行分隔符,               

[root@localhost tmp]# awk -v RS=':' -v ORS='#' '{print}' /etc/passwd
root#x#0#0#root#/root#/bin/bash

                             NF:表示每行的字段数量                

[root@localhost tmp]# awk -F: '{print NF}' /etc/fstab 
0
1
1

                     如果在{ }里面加上个$,$NF表示的是每行的最后一个字段,所以注意在括号里面引用内建变量或已定义的变量不需要加$ 

[root@localhost tmp]# awk -F: '{print $NF}' /etc/passwd
/bin/bash
/sbin/nologin

            NR:打印行数,表示总记录数

                      FNR:打印行数,表示当前的记录数

[root@localhost tmp]# awk  '{print NR}' file1 file2
1
2                                //NR=FNR表示只处理12345行的内容,相当于只处理file1的内容
3
4
5
6
7
[root@localhost tmp]# awk  '{print FNR}' file1 file2
1
2                              //NR>FNR表示处理567行的内容,相当于只处理file2的内容
3
4
1
2
3

  示例:有两个文件file1、file2,内容分别是     

           1:beijing:1322676:::                1:x:d3dn1  

           2:shanghai:222676:::              2:y:DWHh0

           3:Guangdong:342676:::          3:z:hWX9Y

    求 awk 'BEGIN{OFS=FS=":"}NR==FNR{a[$1]=$2;}NR>FNR{$2=a[$1];print}' file1 file2 的结果     

[root@localhost tmp]# awk 'BEGIN{OFS=FS=":"}NR==FNR{a[$1]=$2;}NR>FNR{$2=a[$1];print}' file1 file2
1:beijing:d3dn1
2:shanghai:DWHh0
3:Guangdong:hWX9Y

    NR==FNR{a[$1]=$2;} :把file1中的$2赋值给a[$1]数组中,分别是1=beijing  2=shanghai  3=Guangdong

       NR>FNR{$2=a[$1]     :把数组的元素赋值给file2中$2,然后输出

                        

           FILENAME:输出当前文件名

                          文件有几行就输出多少次名字        

[root@localhost tmp]# awk '{print FILENAME}' /etc/issue
/etc/issue
/etc/issue
/etc/issue

                  ARGC:命令行参数的个数   

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

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

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

         自定义变量:

                      1、 -v var=value    //变量名区分大小写

[root@localhost tmp]# awk -v test="hello" 'BEGIN{print test}'
hello

              2、也可以在{ }中直接给出

[root@localhost tmp]# awk  'BEGIN{test="hello";print test}'  
hello                        //注意print前面要有;

         常用的action:

                    1、print em1,em2.....                       

                        要点:

                                   1.逗号分隔符;

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

                                          {print "hello",$1,$2}

                                          {print "hello",$1,$2,6}

                                  3.如省略item,相当于print $0;

                    2、printf 

                            格式化输出:printf  FORMAT ,item1,item2,...

                                                         格式符     

                                     1)、FORMAT是必须要给出

                                     2)、不会自动换行,需要显示则给出换行符,\n

                                     3)、FORMAT中需要分布为后面的每个ietm指定一个格式化符号;

                                              多个FORMAT用可以自己用(:,空格)随便指定,显示时会根据指定的分隔符显示


                                  支持的格式符有:                   

                                                         %c:显示字符的ASCII码

                                             %d,%i:  显示十进制整数

                                             $e,$E:  科学计数法数值显示                   

                                             %f   :  显示为浮点数

                                             %g,%G:   

                                             %s:   显示字符串

                                             %u:   无符号整数

                                             %%:   显示%本身                        

[root@localhost tmp]# awk -F: '{printf "Username: %s  UUID: %d\n",$1,$3}'  /etc/passwd
Username: root  UUID: 0
Username: bin  UUID: 1

                 支持的修饰符有:

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

                                           表示方式:%3s    %3.1s 

                                       - :左对齐,默认右对齐                    

[root@localhost tmp]# awk -F: '{printf "Username: %-8s  UUID: %-2d\n",$1,$3}'  /etc/passwd
Username: root      UUID: 0 
Username: bin       UUID: 1

          awk也支持操作符:         

                      算术操作符:

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

                                        -x

                                        +x:转换为数值;

                                        

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

                            赋值操作符:

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

                                         ++,--                                         

                               比较操作符:

                                         >,>=,<,<=,!=,==                                                                                                                 模式匹配符:

                                         ~:是否匹配右侧字符串

                                         !~  是否不匹配右侧字符串             

                            逻辑操作符:

                                           && :

                                           || :

                                           ! :                                    

                      函数调用:

                                                           function_name(argu1,argu2,...)

           awk还支持内建的条件表达式:                   

                 selector?     if-true-expression:   if-false-expression

                           条件表达式   如果为真执行这个        为假执行这个      

[root@localhost tmp]#  awk -F: '{$3>1000?usertype="Common User":usertype="System User";printf "%-5s:%-1s\n",usertype,$1}' /etc/passwd 
System User:root
System User:bin

    awk支持匹配模式:       

                         1、empty:空模式,匹配每一行

                         2、/regular expression/:仅处理能够被此处的模式匹配到的行

[root@localhost tmp]# awk '/^UUID/{print}' /etc/fstab 
UUID=98a6a73c-6b57-479f-947f-2d201a17c950 /                   ext4    defaults        1 1
UUID=361beea7-5d62-4c33-b317-c93fbc4e0650 /boot               ext4    defaults        1 2
UUID=d2750a4a-9a19-45c7-a54e-3bb22db4c1b3 swap                swap    defaults        0 0

                         3、relational expression:关系表达式:结果有“真”有“假”;结果为真才会被处理                                                                                   真:结果为非0值,非空字符串     假:结果为0值

[root@localhost tmp]# awk -F: '$3>1000{print $1,$3}' /etc/passwd
user11 2000
mageedu 3001
mageed 3002
auser2 3003
msql 3004
gentoo 3005
centos 3006
[root@localhost tmp]# awk -F: '$NF~"/bash$"{print $1,$7}' /etc/passwd
root /bin/bash
user1111 /bin/bash
user222 /bin/bash

                   4、line ranges:行范围,地址定界

                          startline,endline        不支持直接给出数字的格式

                           /pat1/,/pat2/       

[root@localhost tmp]# awk -F: '/^root/,/^daemon/{print $1}' /etc/passwd
root
bin
daemon

                 5、BEGIN/END模式

                               BEGIN{}:在开始处理文件中文本之前仅执行一次,无论文本是否存在                                    

[root@localhost tmp]# awk 'BEGIN{print "Username\n--------------"}'
Username
--------------
[root@localhost tmp]# awk -F: '{print "Username\n--------------\n",$1}' /etc/passwd
Username                  //不加BEGIN每行都会输出一次
--------------
 root
Username
--------------
 bin

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

[root@localhost tmp]# awk -F: '{print "Username\n--------------\n"}{print $1}END{print "#######"}' /etc/passwd
Username
--------------
named
#######

        awk还支持控制语句:

                           1、‘if语句

                                   {if(condition) {statements}’ //使用在对awk去的整行或某个字段做条件判断

                                ‘{if(condition) {statments} else {statements}’

[root@localhost tmp]# awk -F: '{if($3>1000) {print $1,$3}}' /etc/passwd
user11 2000
mageedu 3001
[root@localhost tmp]# awk -F: '{if($3>1000) {print "User:" $1}else {print "SystemUser:" $1}}' /etc/passwd
SystemUser:root
SystemUser:bin

             2、while循环          // 对一行内的多个字段逐一类似处理时使用;对数组中的各元素逐一处理时使用。

                                    语法: while(condition) {statement}

                                          条件“真”,进入循环:条件“假”,退出循环

      比如要计算某个文件里每个字段的的字符长度,那就需要用到while循环

[root@localhost tmp]# awk -F' '  '/^[[:space:]]*kernel/{i=1;while(i<=NF) {if(length($i)>=7) {print $i,length($i)};i++}}' /etc/grub.conf
/vmlinuz-2.6.34.14 18
root=UUID=98a6a73c-6b57-479f-947f-2d201a17c950 46

               i:表示字段,length($i):表示返回字符长度,i++:表示显示下一个字段

           3、for循环

                                     语法: ‘{for(expr1;expr2;expr3)  statement}’

[root@localhost tmp]# awk -F' '  '/^[[:space:]]*kernel/{for(i=1;i<=NF;i++) {print $i,length($i)};i++}' /etc/grub.conf
kernel 6
/vmlinuz-2.6.34.14 18
ro 2
root=UUID=98a6a73c-6b57-479f-947f-2d201a17c950 46

         特殊用法:

                         能够遍历数组中的元素:

                          语法:for(var in  array) {for-body}

[root@localhost tmp]# awk 'BEGIN{weekday["mon"]="Monday";weekday["tue"]="Tuesday";for(i in weekday) {print weekday[i]}}'
Monday
Tuesday

         4、break和continue

                 5、next   提前结束对本行的处理而进入下一行

[root@localhost tmp]# awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd
root 0
daemon 2    //取出偶数行

      awk还支持数组                  

                支持关联数组: array[index-expression]

                           数组元素下标是从1开始

                index-expression:

                                      1、可使用任意字符串,字符串要用" "括起来

                                      2、如果某数组元素事先不存在,在引用时,awk会自动

                                         创建此元素,并将其值初始化为“空“

    示例:统计/etc/fstab文件中每个文件系统出现的次数   

[root@localhost tmp]# awk '/^UUID/{count[$3]++}END{for (i in count){print i,count[i]}}' /etc/fstab 
swap 1
ext4 2

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

       count[$3]++:初始值为空,每读到一个相同元素就加1

      i:表示数字索引   count[i]:表示数组元素

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

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

    awk还支持函数:                   

       内置函数

                          数值处理:

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

[root@localhost tmp]# awk 'BEGIN{print rand()}'
0.237788

             

            字符串处理:

                                  length([s]):返回指定字符串的长度

                                  sub(r,s,[t]):以r表示的模式来查找t所表示的字符中的匹配的内容

                                                并将其第一次出现出现替换为s所表示的字符串。                

                                  gsub(r,s,[t]):以r表示的模式来查找t所表示的字符中的匹配的内容

                                                并将其替换为s所表示的字符串。 

                                  split(s,a[,r]):以r为分隔符切割字符s,并将切割后的结果保存至a所表示的数字中;

      示例:求出访问本机中的服务器的总数     

[root@localhost tmp]# netstat -tan |awk  '/^tcp/{split($5,ip,":");print ip[1]}' |awk -v RS='' '{print}'|awk '{count[$1]++}END{for (i in count) {print i,count[i]}}'            
0.0.0.0 2
172.18.252.65 1