AWK进阶

上一篇介绍了AWK的一些基本使用规则和一些示例,本篇介绍AWK进阶的一些知识


PATTERN 模式匹配,有点类似于sed中的地址定界,用来过滤符合要求的记录

    (1)默认是空模式,匹配每一行

    (2)/regular expression/ :表示过滤出符合模式的的记录,正则表达式

        image.png

    (3)relational expression:关系表达式,这里结果是布尔值,有“真”有“假”,只有结果为“真”才处理

        这里的“真”即结果是非零或非空的表示真,其它为“假”。

        例如,以:为域分隔符,对符合表达式的行进行打印。这里的意思即当$3小于等于30的行才打印

         image.png

         例如,以:为分隔符,$3小于等于1000并且最后一个域是字符串/bin/bash 的行,然后打印

         image.png

    (4)行范围定界

        类似于sed,/pat1/,/pat2/。

        例如,匹配第一个以h开头的行到第一个以a开头的行并打印

          image.png

        注意:这里不能像sed那样直接通过数字界定

    (5)BGEIN/END模式

        BEGIN{} 仅在开始读入记录之前执行一次

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

        [root@centos7 11:18:52 ~]#awk -F: 'BEGIN{print "username       UID       \n---------------------------"}$3<=1000 && $NF=="/bin/bash"{printf "%-15s%d\n",$1,$3}END{print "=======================\nend"}' /etc/passwd
        username       UID       
        ---------------------------
        root           0
        zmh            1000
        =======================
        end

    (6)progranm中常用的action

        a.Expressions

        b.条件控制语句:if,while等

        c.组合语句

        d.inpue statements

        e.output statements

    (7)控制语句

        if(condition){statments}

        if(condition){statments}else{statements}

        while(condition){statements}

        do{stetements}while(condition)

        for(expr1;expr2;expr3){statements}

        break

        continue

        delete array[index]

        delete array

        exit


控制语句

    1.if-else

        语法:if(condition)statement[else statement]

        [root@centos7 14:50:38 ~]#awk -F: '{if($3<=50&&$3!=0)print}' /etc/passwd
        bin:x:1:1:bin:/bin:/sbin/nologin
        daemon:x:2:2:daemon:/sbin:/sbin/nologin
        adm:x:3:4:adm:/var/adm:/sbin/nologin
        lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
        sync:x:5:0:sync:/sbin:/bin/sync
        shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
        halt:x:7:0:halt:/sbin:/sbin/halt
        mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
        operator:x:11:0:operator:/root:/sbin/nologin
        games:x:12:100:games:/usr/games:/sbin/nologin
        ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
        rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin
        rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
        gdm:x:42:42::/var/lib/gdm:/sbin/nologin
        ntp:x:38:38::/etc/ntp:/sbin/nologin
        apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
        
        [root@centos7 14:54:51 ~]#awk -F: '{if($3<=20){print $1,$3}else{print $1}}' /etc/passwd
        root 0
        bin 1
        daemon 2
        adm 3
        lp 4
        sync 5
        shutdown 6
        halt 7
        mail 8
        operator 11
        games 12
        ftp 14
        nobody
        systemd-network
        dbus
        polkitd
        abrt
        libstoragemgmt
        rpc
        
注意,如果if是单分支则statements不用使用{}括起来,如果是if else双分支语句那么statement需要使用{}括起来

         

        示例,取出磁盘使用率大于指定值的磁盘

        [root@centos7 15:09:30 ~]#df | awk -F% '/^\/dev\/sd/{print $1}' 
        /dev/sda2       52403200 3876228  48526972   8
        /dev/sda3       31441920  354300  31087620   2
        /dev/sda1        1038336  161628    876708  16
        [root@centos7 15:09:38 ~]#df | awk -F% '/^\/dev\/sd/{print $1}' | awk '{if($NF>=10)print $1,$NF}'
        /dev/sda1 16

    2.while循环

        语法:shile(condition)statement

        条件为“真”进入循环;条件为“假”退出循环。如果第一次判断就是“假”那么就不会进入循环

        使用场景:对一行内的多个字段逐一进行处理时使用;对数组中的各元素进行逐一处理

        例如,统计/opt/httpd-2.2.34/README中每个字符出现次数并打印字符及字符长度(length()函数引用)

        [root@centos7 16:09:53 usr]#awk '{i=1;while(i<NF){print $i,length($i);i++}}' /opt/httpd-2.2.34/README| sort -t' ' -k 2 -nr | uniq -c |sort -nr
     37 the 3
     19 of 2
     18 Apache 6
     17 to 2
     15 and 3
     13 for 3
     10 The 3
      9 in 2
      8 a 1
      7 is 2
      7 be 2
      7 as 2
      6 you 3
      6 this 4

    3.do-while循环

        语法:do{statment;...}while(condition) 无论真假,至少执行一次循环

        示例,计算1..100整数之和

        [root@centos7 16:10:13 usr]#awk 'BEGIN{sum=0;i=0;do{sum+=i;i++}while(i<=100){print sum}}'
        5050
        [root@centos7 16:18:31 usr]#

    4.for循环

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

        遍历数组元素:for(var in array){statement}

        示例,遍历grub.cfg文件,并存入word[]数组,然后遍历数组元素并答应值

        [root@centos7 16:37:01 usr]#awk '{for(i=1;i<=NF;i++){word[$i]++}}END{for(j in word){print j,word[j]}}' /boot/grub2/grub.cfg 
        insmod 14
        savedefault 1
        tuned_params="" 1
        ro 2
        --hint='hd0,msdos1' 2
        boot_once=true 2
        'gnulinux-3.10.0-693.el7.x86_64-advanced-6aaae4c2-65bf-40c9-a001-8ab05ccbc30d' 1
        x"${feature_menuentry_id}" 1
        type 1
        an 1
        /etc/grub.d/41_custom 2
        after 1
        root='hd0,msdos1' 2

        示例,用不同方法 求1..1000000之和,用time计算所耗时间

        [root@centos7 16:55:55 usr]#time (awk 'BEGIN{sum=0;for(i=0;i<=1000000;i++){sum+=i}print sum}')
        500000500000
        real 0m0.119s
        user 0m0.116s
        sys 0m0.004s
        [root@centos7 16:55:56 usr]#time (sum=0;for i in {1..1000000};do let sum+=$i;done;echo $sum)
        500000500000
        real 0m5.864s
        user 0m5.685s
        sys 0m0.174s
        [root@centos7 16:56:08 usr]#time (seq -s "+" 1 1000000|bc)
        500000500000
        real 0m0.441s
        user 0m0.280s
        sys 0m0.245s
        [root@centos7 16:56:47 usr]#

            以上可以看出awk计算效率远比shell高

    5.switch语句

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

    6.break和continue

        示例,

        [root@centos7 17:09:33 usr]#awk 'BEGIN{sum=0;for(i=0;i<=100;i++){if(i%2==0)continue;sum+=i}print sum}'
        2500
        [root@centos7 17:11:25 usr]#awk 'BEGIN{sum=0;for(i=0;i<=100;i++){if(i==88)break;sum+=i}print sum,i}'
        3828 88

    7.next:提前结束对本行处理而直接进入下一行处理(awk自身循环读入行的循环)

        示例,

        [root@centos7 17:15:05 usr]#awk -F: '{if($3%2==1)next;print $1,$3,NR}' /etc/passwd
        root 0 1
        daemon 2 3
        lp 4 5
        shutdown 6 7
        mail 8 9
        games 12 11
        ftp 14 12
        systemd-network 192 14
        libstoragemgmt 998 18
        rpc 32 19
        saslauth 996 21
        rtkit 172 22
        nfsnobody 65534 26
        geoclue 994 29
        gdm 42 33
        gnome-initial-setup 992 34
        sshd 74 35
        avahi 70 36
        ntp 38 38
        tcpdump 72 39
        zmh 1000 40
        docker 1002 42
        base 1004 44
        testbash 1006 46
        nologin 1008 47
        apache 48 49
        user1 1010 50


awk数组

    awk中数组都是关联数组,array[index-expression]

    index-expression:

        (1)可使用任意字符串;字符串要使用双引号括起来

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

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

        示例,array[$0]事先不存在,awk自动创建并赋值为空,再!取反则值为1,表示真,然后执行默认{print $0}

        [root@centos7 18:44:47 usr]#awk '!arr[$0]' /etc/fstab
        #
        # /etc/fstab
        # Created by anaconda on Wed Mar 28 02:08:30 2018
        #   Created by anaconda on Wed Mar 28 02:08:30 2018
        #
        # Accessible filesystems, by reference, are maintained under '/dev/disk'
        # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
        #
        UUID=6aaae4c2-65bf-40c9-a001-8ab05ccbc30d /                       xfs     defaults        0 0
        UUID=1e010872-4180-4ce5-be38-4152a4f302e5 /boot                   xfs     defaults        0 0
        UUID=66f9e8a4-7d65-4ced-8bd9-f76e5e7b97d6 /data                   xfs     defaults        0 0
        UUID=af954884-fc86-4450-9c1d-342531c53c7b swap                    swap    defaults        0 0
        /dev/sr0       /data/mnt/6/x86_64/   iso9660   defaults   0 0
        /dev/sr1       /data/mnt/7/x86_64/   iso9660   defaults   0 0
        [root@centos7 18:44:53 usr]#

        若要遍历数组中的每个元素,要使用for循环。for(var in array){for-body}

        注意:var会遍历array的每个索引

        示例,筛选以tcp开头的网络端口状态,并存储在state[]数组中,再使用for遍历数组并打印数组索引(即端口状态),和数组元素(即相同状态个数)

        [root@centos7 18:44:53 usr]#netstat -tan | awk '/^tcp/{state[$NF]++}END {for(i in state) { print i,state[i]}}'
        LISTEN 9
        ESTABLISHED 1


awk函数

    数值处理函数

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

        示例,

        [root@centos7 18:57:55 usr]#awk 'BEGIN{srand(); for (i=1;i<=10;i++)print int(rand()*100) }'
        13
        92
        18
        63
        39
        4
        42
        12
        18
        92

    字符处理函数:

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

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

        [root@centos7 18:57:56 usr]#awk 'sub(/:/,"---",$1)' /etc/passwd
        root---x:0:0:root:/root:/bin/bash
        bin---x:1:1:bin:/bin:/sbin/nologin
        daemon---x:2:2:daemon:/sbin:/sbin/nologin
        adm---x:3:4:adm:/var/adm:/sbin/nologin
        lp---x:4:7:lp:/var/spool/lpd:/sbin/nologin
        sync---x:5:0:sync:/sbin:/bin/sync
        
        [root@centos7 19:02:17 usr]#awk 'gsub(/:/,"---",$1)' /etc/passwd
        root---x---0---0---root---/root---/bin/bash
        bin---x---1---1---bin---/bin---/sbin/nologin
        daemon---x---2---2---daemon---/sbin---/sbin/nologin
        adm---x---3---4---adm---/var/adm---/sbin/nologin
        lp---x---4---7---lp---/var/spool/lpd---/sbin/nologin
        sync---x---5---0---sync---/sbin---/bin/sync

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

    split(s,array,[r]):以r为分隔符,切割字符串s,并将切割后的结果保存至array所表示的数组中,第一个索引值为1,第二个索引值为2,…

        示例,

        [root@centos7 19:06:50 usr]#netstat -tan | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}END{for (i in count) {print i,count[i]}}'
        192.168.30.1 1
        0.0.0.0 4

    自定义函数 

    格式:
    function name ( parameter, parameter,...){
        statements

        return expression
    } 

    示例:比较a,b的大小
        cat fun.awk

        function max(v1,v2){

            v1>v2?var=v1:var=v2

            return var
        }

        BEGIN{a=3;b=2;print max(a,b)} awk –f fun.awk

    awk -f fun.awk

    示例,求男生(m)的平均成绩,女生(f)的平均成绩

    [root@centos7 19:15:56 bin]#cat score.txt 
    leifeng 100 m
    lilei 99 m
    limengmeng 90 f
    han××× 100 f
    [root@centos7 19:16:07 bin]#awk '{num[$NF]++;sum[$NF]+=$2 }END{for(sex in num)printf "%s:%.2f\n",sex,sum[sex]/num[sex]}' score.txt
    m:99.50
    f:95.00
    [root@centos7 19:16:09 bin]#


awk中调用shell命令

    system命令,空格是awk中的字符串连接符,如果system中需要使用awk中的变量可以使用空格分隔,或者说除了awk的变量外其他一律用""引用起来。

    示例,

    [root@centos7 19:16:09 bin]#awk 'BEGIN{score=100; system("echo your score is " score) }'
    your score is 100


向awk脚本传递参数

    格式:
        awkfile var=value var2=value2... Inputfile
    注意:在BEGIN过程中不可用。直到首行输入完成以后,变量才可用。可以通 过-v 参数,让awk在执行BEGIN之前得到变量的值。命令行中每一个指定的变 量都需要一个-v参数
    示例,

        [root@centos7 19:25:23 bin]#cat test.awk 
        #!/bin/awk -f 
        {if($3 >=min && $3<=max)print $1,$3}
        [root@centos7 19:25:26 bin]#chmod +x test.awk 
        [root@centos7 19:25:31 bin]#test.awk -F: min=100 max=200 /etc/passwd
        systemd-network 192
        abrt 173
        rtkit 172
        qemu 107
        usbmuxd 113
        pulse 171
        [root@centos7 19:25:33 bin]#




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章