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]#




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