Centos 6.6上awk的簡單使用

      本文實驗環境:vmware workstation 10 +Centos6.6 X86_64,文中命令請謹慎使用。

         AWK是類UNIX操作系統中一款文本處理工具,同grep,sed併成爲文本處理三劍客,使用上的側重點爲:生成格式化的文本報告,但awk有豐富的語法和靈活的變量,稱其爲一門獨立的語言也不爲過。下文僅僅是awk生成格式化文本報告功能的簡單使用。

        1.awk工作過程描述:

          awk將被處理的文件,逐行讀入;按照輸入分隔符,對改行進行分割處理;分割後的行內容塊的索引從1開始,索引0爲整行內容;根據定義的處理方式對行內容進行匹配,若匹配條件,則以合適的格式打印相應的行數據塊,若行數據不匹配相應條件,則不處理。

        2.awk語法格式

            對awk來說很難總結出一個完全適應任何場合的語法格式,就生成格式化文本報告功能來說可以勉強寫爲下面的:

         $awk [option] '[PATTERN] { ACTION STATEMENT }' file

          option:

                    經常用到的有2個:

                    -F PUNCT:指定輸入分隔符。

                    -v  VAL_NAME=VALUE:自定義變量值。下文有例子說明。

            其它的部分,請根據下面的例子進行學習,文末尾再進行總結。

        3.例子教學:

                3.1)打印/etc/passwd文件中root用戶條目的用戶名,uid和shell。     

       先取出/etc/passwd文件中root用戶的條目  

[lijun@Test02 ~]$ grep  '^root\>'  /etc/passwd
root:x:0:0:root:/root:/bin/bash
#因沒說到行模式匹配,故這裏借用grep搜索root開頭的行

       可以看到關於root用戶的條目處理時使用“:”爲分隔符是個明智的選擇,用戶名在第一部分,uid在第三部分,shll在第七部分,所以可以使用awk這樣來寫,就滿足題意:

[lijun@Test02 ~]$ grep '^root\>' /etc/passwd | awk -F ':' '{print $1,$3,$7}'
root 0 /bin/bash
[lijun@Test02 ~]$
#因沒說到行模式匹配,故這裏借用grep搜索root開頭的行

       上述awk表達式中,使用-F指定輸入分隔符爲“:”; ACTION爲 print,就是打印的意思, STATEMENT是 $1,$3,$7  

當然也可這樣寫:

[lijun@Test02 ~]$ grep '^root\>' /etc/passwd | awk -v FS=':' '{print $1,$3,$7}'
root 0 /bin/bash
[lijun@Test02 ~]$
#因沒說到行模式匹配,故這裏借用grep搜索root開頭的行

   總結一:ACTION: print

   print:

     格式:
            print item1,item2,...
            要點:
                 1)各item間使用逗號分隔,而輸出時使用輸出分隔符分隔,輸出分隔符默認爲空格。
                 2)輸出的各item爲字符串,數值和當前記錄的字段($n),變量的值或awk的表達式,
                    數值會被隱式轉換爲字符進行輸出。
                 3)print後面的item如果省略,相當於print $0;輸出空白 相當於print " "


       上述2種awk表達方式可以完成題目的要求,但是輸入的內容不是很美觀,可以使用printf 代替print 來打印輸出格式:

[lijun@Test02 ~]$ grep '^root\>' /etc/passwd | awk -F ':' '{printf "USERNAME:%-s\nUID:%-d\nSHELL:%-s\n",$1,$3,$7}'
USERNAME:root  
UID:0
SHELL:/bin/bash
[lijun@Test02 ~]$
#因沒說到行模式匹配,故這裏借用grep搜索root開頭的行

      "USERNAME:%-s\nUID:%-d\nSHELL:%-s\n" 是格式符,因後邊有3個部分要打印,故這裏給出了3個格式符,USERNAME:%-s\n是針對第一個item即題目中的要打印的用戶名,UID:%-d\n是針對第二個item即題目中的要打印的UID,SHELL:%-s\n針對的是第三個item即用戶的shell。並且每個格式符中明確指定了換行符\n

雖然好看了,但是awk表達式也複雜起來,下面就總結下ACTION中printf的使用:

   總結二:ACTION:  printf:
         格式:
            printf  format,item1,item2,...
            要點:
                 1)printf中format稱爲格式符,是必須的,需明確給出
                 2)不會自動換行,需顯示指定換行符\n
                 3)format中需要分別爲後面每一個item指定一個格式符
                
                 格式符:都以%開頭,後跟一個字符
                     %c:顯示字符ASCII碼,其實顯示字符本身
                     %d %i:顯示十進制格式的整數
                     %e %E:科學計數法顯示數值
                     %f:顯示浮點數
                     %g %G:以科學計數法格式或浮點數格式顯示數值
                     %s:顯示字符串
                     %u:顯示無符號整數
                     %%:顯示%自身
                 格式的修飾符:
                     #[.#]:第一個#爲數字,顯示寬度,後邊.#在表達浮點數時表示精度              
                     -:左對齊,默認右對齊          
                     +:顯示數值的符號, 正負數


            3.2)使用"#"爲連接符,顯示系統上root用戶的名稱,uid和shell的設定

[root@Test02 ~]# grep '^root\>' /etc/passwd | awk -v FS=':' -v OFS='#' '{print $1,$3,$7}'
root#0#/bin/bash
[root@Test02 ~]#
#因沒說到行模式匹配,故這裏借用grep搜索root開頭的行

     總結三:awk變量:FS爲行輸入分隔符,OFS爲行輸出分隔符,使用-v val_name=value來定義,-F PUNCT也是指定輸入分隔符的,二者默認爲空格。

            3.3)打印/etc/passwd文件中用戶名爲一行

    因awk是逐行對文件進行處理的,故/etc/passwd文件中每行的$1就是用戶名,但是對輸出換行符不進行指定的話,打印出來是這個樣子:

[root@Test02 ~]# head -3 /etc/passwd | awk -F ':' '{print $1}'
root
bin
daemon
[root@Test02 ~]#

    因此需要指定輸出換行符,這樣纔可將/etc/passwd文件中用戶名打印爲一行

[root@Test02 ~]# awk -v FS=':' -v ORS=' ' '{print $1}' /etc/passwd
root bin daemon adm lp sync shutdown halt mail uucp operator games gopher ftp nobody dbus usbmuxd vcsa rpc rtkit avahi-autoipd abrt rpcuser nfsnobody haldaemon gdm ntp apache saslauth postfix pulse sshd tcpdump lijun [root@Test02 ~]#

總結三:awk變量:RS爲awk輸入換行符,ORS爲輸出換行符,使用-v val_name=value來定義

            3.4)顯示/etc/passwd中第一行有幾個字段,並打印最後那個字段

[root@Test02 ~]# head -1 /etc/passwd | awk -F ':' '{print NF,$NF}'
7 /bin/bash

總結三:awk變量:NF爲當前行可被分割的成幾個字段,$NF爲被分割後最後那個字段,需指明行分隔符,默認爲空格。

            3.5)打印顯示/etc/issue和/etc/sysconfig/network的行和行號,仔細觀察下面三段顯示的不同

方式一:
[root@Test02 ~]# awk '{print NR,$0}' /etc/issue /etc/sysconfig/network
1 CentOS release 6.6 (Final)
2 Kernel \r on an \m
3 
4 NETWORKING=yes
5 HOSTNAME=Test02.lijun.com
[root@Test02 ~]#
方式二:
[root@Test02 ~]# awk '{print FNR,$0}' /etc/issue /etc/sysconfig/network
1 CentOS release 6.6 (Final)
2 Kernel \r on an \m
3 
1 NETWORKING=yes
2 HOSTNAME=Test02.lijun.com
[root@Test02 ~]# awk '{print FILENAME,FNR,$0}' /etc/issue /etc/sysconfig/network
/etc/issue 1 CentOS release 6.6 (Final)
/etc/issue 2 Kernel \r on an \m
/etc/issue 3 
/etc/sysconfig/network 1 NETWORKING=yes
/etc/sysconfig/network 2 HOSTNAME=Test02.lijun.com
[root@Test02 ~]#

總結三:awk變量:一行命令中同時處理多個文件時,NR:後邊要處理的文件的行進行統一計數,顯示當前行的行號。FNR:後邊每個要處理的文件的行數單獨計數時,顯示當前行的行號。FILENAME:當前被處理的文件的文件名。

            3.6)

(1)打印顯示/etc/passwd文件中普通用戶的信息

在centos6.6上系統中的匹配用戶的uid從500開始的,故這裏只需打印/etc/passwd文件中uid列大於等於500的用戶行即可

[root@Test02 ~]# awk -v FS=':' '$3>=500{print $0}' /etc/passwd
nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
lijun:x:500:500:lijun:/home/lijun:/bin/bash
[root@Test02 ~]#

這裏用到可行模式匹配,即awk語法格式中的pattern的定義。

總結四:PATTERN:

    關係表達式,即上例中的'$3>=500

    涉及到比較操作符: >,>=,< ,<=,==,!=

(2)使用ifconfig eth1顯示eth1網卡的配置信息,抓取ip地址所在的行

[root@Test02 ~]# ifconfig eth1
eth1      Link encap:Ethernet  HWaddr 00:0C:29:70:8F:0B  
          inet addr:192.168.100.2  Bcast:192.168.100.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fe70:8f0b/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:3806 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1764 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:350119 (341.9 KiB)  TX bytes:189329 (184.8 KiB)
[root@Test02 ~]#

因IP地址所在的行在第二行,故藉助NR可以抓取出來。

[root@Test02 ~]# ifconfig eth1|awk 'NR==2{print $0}'
          inet addr:192.168.100.2  Bcast:192.168.100.255  Mask:255.255.255.0
[root@Test02 ~]#

            3.7)顯示/etc/passwd文件中root用戶行信息

因在/etc/passwd文件每一行開頭的都是用戶名,故抓取root開頭且爲獨立單詞的行即可,正則表達式爲^root\>

[root@Test02 ~]# awk -F ':' '/^root\>/{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@Test02 ~]#

總結四:PATTERN:

        /regular expression/:僅處理能被/regular expression/匹配的行

上述例子中root若不加詞尾錨釘,會出現下列現象:

[root@Test02 ~]# awk -F ':' '/^root/{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
rootli:x:501:501::/home/rootli:/bin/bash

            3.8)顯示/etc/passwd文件中root開頭的行至shutdown開頭的行之間的內容

[root@Test02 ~]# awk -F':' '/^root/,/^shutdown/{print $0}' /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
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
rootli:x:501:501::/home/rootli:/bin/bash
[root@Test02 ~]#

總結四:PATTERN:

        line rangs:行範圍,類似於sed 或vim 中的定界符,startline,endline

            3.9)打印系統上shell爲bash的用戶,開始和結束要有明確提示:

[root@Test02 ~]# awk -F: 'BEGIN{print "======================"}BEGIN{printf "%-15s%-s\n","username","shell"}$NF~/bash$/{printf "%-15s%-s\n",$1,$NF}END{print "======================"}' /etc/passwd
======================
username       shell
root           /bin/bash
lijun          /bin/bash
rootli         /bin/bash
======================
[root@Test02 ~]#

總結四:PATTERN:

        BEGIN:同要處理的文件內容沒關係,只是在所有awk動作之前執行一個動作
        END:在awk所有的動作結束後,執行的動作

            3.10)顯示系統中的用戶uid在0與100之間的用戶

因用戶的UID在/etc/passwd文件中第3部分,因此只要對/etc/passwd文件使用":"爲輸入行分隔符,抓取第三部分進行條件過濾即可實現

[root@Test02 ~]# awk -F ':' '{if($3>0)if($3<100)printf "%-12s%-d\n",$1,$3}' /etc/passwd
bin         1
daemon      2
adm         3
lp          4
sync        5
shutdown    6
halt        7
mail        8
uucp        10
operator    11
games       12
gopher      13
ftp         14
nobody      99
dbus        81
vcsa        69
rpc         32
rpcuser     29
haldaemon   68
gdm         42
ntp         38
apache      48
postfix     89
sshd        74
tcpdump     72
[root@Test02 ~]#

總結五:awk中的控制語句if;

    格式:

        if(condition){statement1;statement2;...}[else {statement1;statement2;...}]
             當 statement只有一個時,{}可以被省略
             單分支if語句時 {}可省略

        上面的例子中if語句相當於2個if語句的嵌套,若寫成bash腳本格式如下:

        if [ $3 -gt 0 ];then

                if [ $3 -lt 100 ];then

                        echo .....

                fi

         fi

        awk中是不是不用寫關閉符很爽,不用寫bash腳本那樣死板的格式很爽,那就去玩玩python吧,python的腳本寫着同樣很爽,至少比bash 爽。

            3.11)顯示/etc/inittab文件第一句中每個字段的長度

[root@Test02 ~]# head -1 /etc/inittab | awk '{i=1;while(i<=NF){print $i,length($i);i++}}'
# 1
inittab 7
is 2
only 4
used 4
by 2
upstart 7
for 3
the 3
default 7
runlevel. 9
[root@Test02 ~]#
#length($i)是awk的內置函數,用來顯示字符串的長度。

總結五:awk中的控制語句:while

    格式:
         while(condition){statment1;statment2;...}
            條件爲真時循環,直到爲假時退出;
            通常用於在當前行的各字段間進行循環

      在上面的awk語句中定義了變量i,NF爲awk內置變量表示當前行可被分割成的字段數,當變量i<=NF爲真時,while循環啓動,顯示$i爲index的字段,length($i)爲字段的長度,讓後用i++自加,指導i<=NF爲假時,停止while循環,停止打印。

 上面的例子使用另外一種格式來實現:

[root@Test02 ~]# head -1 /etc/inittab | awk '{for(i=1;i<=NF;i++)print $i,length($i)}'
# 1
inittab 7
is 2
only 4
used 4
by 2
upstart 7
for 3
the 3
default 7
runlevel. 9
[root@Test02 ~]#

總結五:awk中的控制語句for

    格式:

        for:
         for(expr1;expr2;expr3) statement
           expr1:變量初始值
           expr2:條件
           expr3:變量變化

            3.12)打印/etc/passwd中uid爲奇數的用戶條目

[root@Test02 ~]# awk -F':' '{if($3%2 !=0)print $0}' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
halt:x:7:0:halt:/sbin:/sbin/halt
operator:x:11:0:operator:/root:/sbin/nologin
gopher:x:13:30:gopher:/var/gopher:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
usbmuxd:x:113:113:usbmuxd user:/:/sbin/nologin
vcsa:x:69:69:virtual console memory owner:/dev:/sbin/nologin
rtkit:x:499:497:RealtimeKit:/proc:/sbin/nologin
abrt:x:173:173::/etc/abrt:/sbin/nologin
rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
pulse:x:497:496:PulseAudio System Daemon:/var/run/pulse:/sbin/nologin
rootli:x:501:501::/home/rootli:/bin/bash
[root@Test02 ~]#

另外一種寫法:

[root@Test02 ~]# awk -F':' '{if($3%2==0)next;print $0}' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
halt:x:7:0:halt:/sbin:/sbin/halt
operator:x:11:0:operator:/root:/sbin/nologin
gopher:x:13:30:gopher:/var/gopher:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
usbmuxd:x:113:113:usbmuxd user:/:/sbin/nologin
vcsa:x:69:69:virtual console memory owner:/dev:/sbin/nologin
rtkit:x:499:497:RealtimeKit:/proc:/sbin/nologin
abrt:x:173:173::/etc/abrt:/sbin/nologin
rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
pulse:x:497:496:PulseAudio System Daemon:/var/run/pulse:/sbin/nologin
rootli:x:501:501::/home/rootli:/bin/bash
[root@Test02 ~]#

總結五:awk中的控制語句,中特殊關鍵字:

    next:提前結束對本行的處理,而進入下一行的處理

    break:跳出當前循環

    continue:提前結束本輪循環,進入下一輪循環

    在上面的例子中使用$3%2:$3對2取模,就是求餘數,若餘數爲0,則被除數$3爲偶數,若餘數不爲0則被除數$3爲奇數。


            3.13)統計當前OS的tcp連接狀態和狀態的個數

顯示os的tcp連接狀態:

[root@Test02 ~]# ss -tan
State       Recv-Q Send-Q                            Local Address:Port                              Peer Address:Port 
LISTEN      0      128                                          :::22                                          :::*     
LISTEN      0      128                                           *:22                                           *:*     
LISTEN      0      128                                   127.0.0.1:631                                          *:*     
LISTEN      0      128                                         ::1:631                                         :::*     
LISTEN      0      128                                           *:60791                                        *:*     
LISTEN      0      100                                         ::1:25                                          :::*     
LISTEN      0      100                                   127.0.0.1:25                                           *:*     
LISTEN      0      5                                             *:5901                                         *:*     
LISTEN      0      128                                          :::111                                         :::*     
LISTEN      0      128                                           *:111                                          *:*     
LISTEN      0      128                                          :::45680                                       :::*     
ESTAB       0      0                                 192.168.100.2:22                             192.168.100.100:49280 
[root@Test02 ~]#

可以看到左邊第一列就是tcp的連接狀態,但是第一行要去掉。

[root@Test02 ~]# ss -tan|awk '!/^State/{connect[$1]++}END{for(i in connect)print i,connect[i]}'
ESTAB 1
LISTEN 11
[root@Test02 ~]#

總結六:awk的數組:

      格式:array_name[index-expression]

          awk中的數組是關聯數組,有的編程語言如python中會稱關聯數組爲字典。
          index-expression:可以使用任意字符串;             
          如果某數組元素不存在,在引用時,awk會自動創建該元素並將其初始化爲空串
          在數組中遍歷每一個元素:使用 for(var in array){for body}
                   var會遍歷array的每一個索引,print array[var]顯示該索引代表的值

      上面的例子中使用/^State/,這個正則表達式匹配#ss -tan的第一行,使用!/^State/ 表示對這個模式取反,即要處理不以State開頭的行。定義了關聯數組connect[],使用$1爲數組的index值,處理每一行時,若connect[$1]存在則數字加1,若connect[$1]不存在則創建。awk動作結束後,使用for語句來處理,上例中i代表connect數組的index,connect[i]則爲該index對應的值。


總結七:awk的內置函數處理字符串時使用:
         字符串處理:

     length([s]):返回指定字符串的長度
     sub(r,s[,t]):以r所代表的模式來查找t字符串中的匹配,將其第一次出現替換同s所表示的字符串
                   #awk -F:'{sub(root,ROOT,$0)}' /etc/passwd

     gsub(r,s[,t]):以r所代表的模式來查找t字符串中的匹配,將所有出現替換爲s所表示的字符串
     split(s,a[,r]):以r爲分隔符切割字符串s,並將切割的結果保存至數組a中,
            len=split(s,a[,r])爲被切割後的段數

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

             <統計連接中,Foreign Address出現的次數>        
     substr(s,i[,n]):從s表示的字符串中取子串,從i開始,取n個字符

    4 awk實際應用舉例:

4.1)取出當前網卡eth1的ipv4地址:

eth1地址格式:

[root@Test02 ~]# ifconfig eth1
eth1      Link encap:Ethernet  HWaddr 00:0C:29:70:8F:0B  
          inet addr:192.168.100.2  Bcast:192.168.100.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fe70:8f0b/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:14759 errors:0 dropped:0 overruns:0 frame:0
          TX packets:13311 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:1331116 (1.2 MiB)  TX bytes:7782755 (7.4 MiB)
[root@Test02 ~]#

處理方式:

[root@Test02 ~]# ifconfig eth1 | awk -F '[ :]+' 'NR==2{print $4}'
192.168.100.2
[root@Test02 ~]#

說明:-F '[ :]+' 指定輸入行分隔符爲 空格和':',+表示出現的次數至少1次,

               inet    addr     :   192.168.100.2    。。。。

第一列   第二列   第三列          第四列          忽略

      NR表示當前處理的行在文件中的位置行號。

      若還是不明白,自己動手試試or畫個圈圈詛咒自己的智商吧!


4.2)統計下面apache access日誌中給出的ip地址和訪問次數

[lijun@Test02 ~]$ cat  3.txt
10.0.0.41 - -[03/Dec/2010:23:27:01 +0800] "HEAD /checkstatus.jsp HTTP/1.0" 200 -
10.0.0.43 - -[03/Dec/2010:23:27:01 +0800] "HEAD /checkstatus.jsp HTTP/1.0" 200 -
10.0.0.42 - -[03/Dec/2010:23:27:01 +0800] "HEAD /checkstatus.jsp HTTP/1.0" 200 -
10.0.0.46 - -[03/Dec/2010:23:27:02 +0800] "HEAD /checkstatus.jsp HTTP/1.0" 200 -
10.0.0.42 - -[03/Dec/2010:23:27:02 +0800] "HEAD /checkstatus.jsp HTTP/1.0" 200 -
10.0.0.47 - -[03/Dec/2010:23:27:02 +0800] "HEAD /checkstatus.jsp HTTP/1.0" 200 -
10.0.0.41 - -[03/Dec/2010:23:27:02 +0800] "HEAD /checkstatus.jsp HTTP/1.0" 200 -
10.0.0.47 - -[03/Dec/2010:23:27:02 +0800] "HEAD /checkstatus.jsp HTTP/1.0" 200 -
10.0.0.41 - -[03/Dec/2010:23:27:03 +0800] "HEAD /checkstatus.jsp HTTP/1.0" 200 -
10.0.0.46 - -[03/Dec/2010:23:27:03 +0800] "HEAD /checkstatus.jsp HTTP/1.0" 200 -

處理方式:

[lijun@Test02 ~]$ awk '{S[$1]++}END{for (a in S)print a,S[a]}' 3.txt | sort -rn -k2
10.0.0.41 3
10.0.0.47 2
10.0.0.46 2
10.0.0.42 2
10.0.0.43 1

    這個就不用說了吧,只是awk管理數組和sort的聯用,簡單的有點可恥。


4.3)打印當前系統上,磁盤分區使用超過10%的分區的分區名,可使用空間和掛載點:

[root@Test02 ~]# df -hP | awk '$1!~/^File/{if($5>="10%")printf "%-10s%-6s%-s\n",$1,$4,$6}'
/dev/sda2 14G   /
/dev/sda1 153M  /boot
[root@Test02 ~]#


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