文本處理三劍客之AWK


AWK的基本格式:linux

   awk [options] 'program' file…      
   註釋: 
   program->pattern{action statements;..}
   pattern和action:
   pattern部分決定動做語句什麼時候觸發及觸發事件
(BEGIN,END)
   action statements對數據進行處理,放在{}內指明
(print, printf)

awk工做原理:shell

    第一步:執行BEGIN{action;… }語句塊中的語句express

    第二步:從文件或標準輸入(stdin)讀取一行,而後執行pattern{ action;… }語句塊,它逐行掃描文件,從第一行到最後一行重複這個過程,直到文件所有被讀取完畢。centos

    第三步:當讀至輸入流末尾時,執行END{action;…}語句塊數組

    注意:pattern語句塊中的通用命令是最重要的部分,也是可選的。若是沒有提供pattern語句塊,則默認執行{ print },即打印每個讀取到的行,awk讀取的每一行都會執行該語句塊bash


   awk [options] 'program' file…      
   program->pattern{action statements;..}
   pattern和action:
   pattern部分決定動做語句什麼時候觸發及觸發事件
(BEGIN,END)
   action statements對數據進行處理,放在{}內指明
(print, printf)
注意:awk程序一般由:BEGIN語句塊、可以使用模式匹配的通用語句塊、END語句塊,共3部分組成。BEGIN語句塊和END語句塊分別爲文家處理前和處理後執行的語句段

1)OPTIONSdom

 

  選項(options):
tcp

    -F指明輸入時用到的字段分隔符(默認以空格爲分隔符)
    -v var內置變量|var=value: 自定義變量ide

    變量分爲內置變量和自定義變量:函數

●內置變量:

FS:輸入字段分隔符,默認爲空白字符(與-F效果相同)

[root@dashui ~]# awk -v FS=":" '{print $1,$3}' /etc/passwd
root 0
bin 1
daemon 2

OFS:輸出字段分隔符,默認爲空白字符

[root@dashui ~]# awk -v FS=":" -v OFS="*" '{print $1,$3}' /etc/passwd
root*0
bin*1
daemon*2

RS:輸入記錄分隔符,指定讀入時的換行符,原換行符仍有效

[root@dashui ~]# awk -v RS=' '  '{print}' hah    //指定讀入時的分割符,只要出現空格就被換行
nihao
a
haha
i
amfanin

[root@dashui ~]# cat hah 
nihao a haha
i amfanin

ORS:輸出記錄分隔符,輸出時用指定符號代替換行符

[root@dashui ~]# cat hah 
nihao a haha
i amfanin
[root@dashui ~]# awk -v ORS='=== '  '{print}' hah     //指定輸出分隔符爲===,因此原來換行被替換成===
nihao a haha=== i amfanin=== [root@dashui ~]#

NF:字段數量

[root@dashui ~]# awk -F: '{print NF}' /etc/passwd    //以:爲分隔符,每一行有幾個字段
7
7
7

NR:行號(若是有多個文件,則行號統計在一塊兒)

[root@dashui ~]# awk '{print NR}' hah
1
2
[root@dashui ~]# awk '{print NR}' hah /etc/fstab       //hah的行號和/etc/fstab的統計在了一塊   
1
2
3
4
5
6
7
8
9
10
11
12
13
14

FNR:各文件分別計數,行號

[root@dashui ~]# awk '{print FNR}' hah /etc/fstab 
1
2
1
2
3
4
5
6
7
8
9
10
11
12

FILENAME:當前文件名

[root@dashui ~]# awk '{print FILENAME}' hah //由於前面的pattern沒有寫,因此默認每讀入一行,就執行一次print
hah
hah
[root@dashui ~]# cat hah 
nihao a haha
i amfanin

ARGC:命令行參數的個數

[root@dashui ~]# awk '{print ARGC}' hah     //其中第一個參數爲awk ,第二個爲 hah
2
2

ARGV:數組,保存的是命令行所給定的各參數

[root@dashui ~]# awk '{print ARGV[0]}' hah     //0是第一個參數,1是第二個參數
awk
awk
[root@dashui ~]# awk '{print ARGV[1]}' hah 
hah
hah

●自定義變量
  (1) -v var=value
    變量名區分字符大小寫
  (2) 在program中直接定義

[root@dashui ~]# awk -v name=xiaoshui 'BEGIN{print name}'
xiaoshui
[root@dashui ~]# awk 'BEGIN{name="xiaozhao";print name}'
xiaozhao

    注意:自定義變量時須要加上雙引號,不加的話可能會定義不成功,以下

[root@dashui ~]# awk 'BEGIN{name=xiaozhao;print name}'

[root@dashui ~]# awk 'BEGIN{name="xiaozhao";print name}'
xiaozhao

2)program

    program分爲pattern和action

  pattern部分決定動做語句什麼時候觸發及觸發事件
(BEGIN,END)
   action statements對數據進行處理,放在{}內指明
(print, printf)

(1)action(print、printf)

Expressions:算術,比較表達式等
Control statements:if, while等
Compound statements:組合語句
input statements
output statements:print等

    print   

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

    (1) 逗號分隔符
    (2) 輸出的各item能夠字符串,也能夠是數值;當前記錄的字段、變量或awk的表達式
    (3) 如省略item,至關於print $0

[root@dashui ~]# awk -F : '{print $1,$3}' /etc/passwd
root 0
bin 1
daemon 2
[root@dashui ~]# awk -F : '{print $1,"xiaoshui",$3}' /etc/passwd
root xiaoshui 0
bin xiaoshui 1
daemon xiaoshui 2
[root@dashui ~]# awk -F : '{print }' /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

    printf

格式化輸出:printf「FORMAT」, item1, item2, ...
    (1) 必須指定FORMAT
    (2) 不會自動換行,須要顯式給出換行控制符,\n
    (3) FORMAT中須要分別爲後面每一個item指定格式符

格式符:與item一一對應

    %c: 顯示字符的ASCII碼
    %d, %i: 顯示十進制整數
    %e, %E:顯示科學計數法數值
    %f:顯示爲浮點數
    %g, %G:以科學計數法或浮點形式顯示數值
    %s:顯示字符串
    %u:無符號整數
    %%: 顯示%自身

修飾符:

    #[.#]:第一個數字控制顯示的寬度;第二個#表示小數點後精度,%3.1f
    -: 左對齊(默認右對齊)%-15s
    +:顯示數值的正負符號%+d

[root@dashui ~]# awk -F : '{printf "%s %d\n",$1,$3 }' /etc/passwd    //若是不加\n默認是不換行的
root 0
bin 1
daemon 2


(2)pattern

PATTERN:根據pattern條件,過濾匹配的行,再作處理

    (1)若是未指定:空模式,匹配每一行
    (2) /regular expression/:僅處理可以模式匹配到的行,須要用/ /括起來

[root@dashui ~]# awk '/^UUID/{print $1}' /etc/fstab // 以UUID開頭的行
UUID=136f7cbb-d8f6-439b-aa73-3958bd33b05f
UUID=bf3d4b2f-4629-4fd7-8d70-a21302111564
UUID=cbf33183-93bf-4b4f-81c0-ea6ae91cd4f6
UUID=5e11b173-f7e2-4994-95b9-55cc4c41f20b

    (3) relational expression: 關係表達式,結果有「真」有「假」,結果爲「真」纔會被處理
      真:結果爲非0值,非空字符串
      假:結果爲空字符串或0值

[root@dashui ~]# awk '0{print $0}' hah    //由於前面爲0,因此不執行
[root@dashui ~]# awk '!0{print $0}' hah    //與上面相反
nihao a haha
i amfanin
[root@dashui ~]# awk -F: '$3==0{print $0}' /etc/passwd    //只有當$3=0時候,纔會執行print
root:x:0:0:root:/root:/bin/bash

    (4) line ranges:行範圍

startline,endline:/pat1/,/pat2/不支持直接給出數字格式

[root@dashui ~]# awk -F: '/^root\>/,/^daemon\>/{print}' /etc/passwd    //打印以root開頭與以daemon開頭的之間的行
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@dashui ~]#

    (5)(5) BEGIN/END模式
BEGIN{}: 僅在開始處理文件中的文本以前執行一次
END{}:僅在文本處理完成以後執行一次

[root@dashui ~]# awk 'BEGIN{print "start"}{print $0}END{print "end"}' hah    //在開始和結束時候分別打印start和end
start
nihao a haha
i amfanin
end
[root@dashui ~]# cat hah
nihao a haha
i amfanin

AWK的操做符

算術操做符:
    x+y, x-y, x*y, x/y, x^y, x%y
    -x: 轉換爲負數
    +x: 轉換爲數值

[root@dashui ~]# awk 'BEGIN{i=1;j=2;sum=i+j;{print sum}}'    
3

字符串操做符:沒有符號的操做符,字符串鏈接
賦值操做符:
    =, +=, -=, *=, /=, %=, ^=
    ++, --
比較操做符:
    >, >=, <, <=, !=, ==
模式匹配符:
    ~:左邊是否和右邊匹配包含    
    !~:是否不匹配

[root@dashui ~]# awk -F: '$1~"root"{print $0}' /etc/passwd    //當$1即usrename爲root或者包含 root時候,執行print
root:x:0:0:root:/root:/bin/bash
chroot:x:1003:1003:,62984566:/home/chroot:/bin/bash
rooter:x:3320:4327:,62984566:/home/rooter:/bin/bash
[root@dashui ~]# awk -F: '$3<2{print $0}' /etc/passwd    //$3即uid小於2時,執行print
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin

邏輯操做符:與&&,或||,非!

[root@dashui ~]# awk -F: '$3>1000 && $3<2000{print $0}' /etc/passwd
xiaoshui:x:1001:1001:xiaoshui,342342342:/home/xiaoshui:/bin/bash
chroot:x:1003:1003:,62984566:/home/chroot:/bin/bash
centos:x:1004:1004:,62984566:/home/centos:/bin/bash
[root@dashui ~]# awk -F: '$1=="root" || $1=="xiaoshui"{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
xiaoshui:x:1001:1001:xiaoshui,342342342:/home/xiaoshui:/bin/bash

awk控制語句

●{ statements;… } 組合語句
if(condition) {statements;…}
if(condition) {statements;…} else {statements;…}
while(conditon) {statments;…}
do {statements;…} while(condition)
for(expr1;expr2;expr3) {statements;…}
break
continue
delete array[index]
delete array
exit

(1){ statements;… } 組合語句

[root@dashui ~]# awk '{a=1;print a}' hah    //將多個語句組合在一塊兒
1
1

(2)if(condition) {statements;…}

[root@dashui ~]# awk -F: '{if($3==0){print $0}}' /etc/passwd
root:x:0:0:root:/root:/bin/bash

    注意:條件在小括號中,條件後的語句需在{ }中

(3)if(condition) {statements;…} else {statements;…}

        if(condition1){statement1}else if(condition2){statement2}else{statement3}

[root@dashui ~]# awk '{if($0=="dashui"){print "dashui"} else{print "not dashui"}}' hah
not dashui
not dashui
dashui
[root@dashui ~]# cat hah
nihao a haha
i amfanin
dashui

(4)while(conditon) {statments;…}

//下面是找到以空格開頭後跟linux16的行,將匹配到的行中的每一個字段的長度與8比較,大於8的輸出
[root@dashui ~]# awk '/^[[:space:]]+linux16/{i=1;while(i<=NF){if(length($i)>8){print $i,length($i)};i++}}' /etc/grub2.cfg 
/vmlinuz-3.10.0-327.el7.x86_64 30
root=UUID=136f7cbb-d8f6-439b-aa73-3958bd33b05f 46
net.ifnames=0 13
/vmlinuz-0-rescue-3f3be6c44d1047a8b32f91efd2f2c5ab 50
root=UUID=136f7cbb-d8f6-439b-aa73-3958bd33b05f 46
net.ifnames=0 13

    作上面的實驗時,由於手抖輸出了點,將if後面的print中i前面的$符號給忘記了,結果就成了下面的狀況

[root@dashui ~]# awk '/^[[:space:]]+linux16/{i=1;while(i<=NF){if(length($i)>8){print i,length(i)};i++}}' /etc/grub2.cfg 
2 1
3 1
7 1
2 1
3 1
7 1
//分析:print i,由於前面i賦值的是數字,本意是想打印$i,即各個字段,當$符號丟掉時,i也只是表明數字了
因此打印出來的237237表示的就是第幾個字段,而length(i)就更好理解了,由於length()就是求括號中的
字符串的長度,且由於i都爲個位數,因此就爲1嘍(注意:若是單單是length(i),i以前沒有被定義,此時打印出來
的爲0,覺得i以前沒有被定義,因此爲空)

(5)do-while循環

語法:do {statement;…}while(condition)
意義:不管真假,至少執行一次循環體

[root@dashui ~]# awk 'BEGIN{i=0;do{i++}while(i<1){print i}}'
1
[root@dashui ~]# awk 'BEGIN{i=0;do{i++}while(i<=1){print i}}'
2

(6)for循環

語法:for(expr1;expr2;expr3) {statement;…}

[root@dashui ~]# awk '/^[[:space:]]+linux16/{for(i=1;i<NF;i++){print $i}}' /etc/grub2.cfg 
linux16
/vmlinuz-3.10.0-327.el7.x86_64
root=UUID=136f7cbb-d8f6-439b-aa73-3958bd33b05f
ro
rhgb
quiet
linux16
/vmlinuz-0-rescue-3f3be6c44d1047a8b32f91efd2f2c5ab
root=UUID=136f7cbb-d8f6-439b-aa73-3958bd33b05f
ro
rhgb
quiet

特殊用法:可以遍歷數組中的元素;
語法:for(varin array) {for-body}

[root@dashui ~]# awk -F: '{bash[$7]++}END{for (i in bash){print i,bash[i]}}' /etc/passwd
/etc/nologin 2
/bin/sync 1
/bin/bash 47
/shell/csh 1
/sbin/nologin 34
/sbin/halt 1
/bin/csh 2
/sbin/shutdown 1
[root@dashui ~]#

(7)switch語句

語法:switch(expression) {case VALUE1 or /REGEXP/: statement; case VALUE2 or /REGEXP2/: statement; ...; default: statement}

(8)breakcontinue

    break表示跳出循環,而continue表示跳過本輪循環

[root@dashui ~]# awk 'BEGIN{sum=0;for(i=1;i<10;i++){if(i==5){break}print i}}'
1
2
3
4
[root@dashui ~]# awk 'BEGIN{sum=0;for(i=1;i<10;i++){if(i==5){continue}print i}}'
1
2
3
4
6
7
8
9

(9)next

    提早結束對本行處理而直接進入下一行處理(awk自身循環)

#$3%2!=0即uid爲奇數,跳過此行,即只打印uid爲偶束的行
[root@dashui ~]# awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd
root 0
daemon 2
lp 4
shutdown 6
mail 8
games 12



awk數組

    關聯數組:array[index-expression]

index-expression:

(1) 可以使用任意字符串;字符串要使用雙引號括起來
(2) 若是某數組元素事先不存在,在引用時,awk會自動建立此元素,並將其值初始化爲「空串
若要判斷數組中是否存在某元素,要使用「index in array」格式進行遍歷

#注意:若是數組的鍵值爲字符串時,最後以雙引號給引發來
[root@dashui ~]# awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";print weekdays["mon"]}'
Monday

若要遍歷數組中的每一個元素,要使用for循環

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


awk函數

●數值處理:
rand():返回0和1之間一個隨機數

#注意:需配合srand()函數使用,否則不是隨機數
[root@dashui ~]# awk 'BEGIN{srand(); for (i=1;i<=10;i++)print int(rand()*100)}'
54
45
23
24
14
72
56
92
31
31


●字符串處理:

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

sub(r,s,[t]):對t字符串進行搜索r表示的模式匹配的內容,並將第一個匹配的內容替換爲s

gsub(r,s,[t]):對t字符串進行搜索r表示的模式匹配的內容,並所有替換爲s所表示的內容

[root@dashui ~]# echo "2008:08:08 08:08:08"|awk 'sub(":","-",$0)'
2008-08:08 08:08:08
[root@dashui ~]# echo "2008:08:08 08:08:08"|awk 'gsub(":","-",$0)'
2008-08-08 08-08-08
[root@dashui ~]#

split(s,array,[r]):以r爲分隔符,切割字符s,並將切割後的結果保存至array所表示的數組中,第一個索引值爲1,第二個索引值爲2,…

#這裏使用split函數將第五個字段中的ip和端口號分開,只取出了ip。
[root@dashui ~]# netstat -tan | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}END{for (i in count) {print i,count[i]}}'
10.1.0.82 1
0.0.0.0 3
[root@dashui ~]# 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: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     
tcp        0     52 10.1.0.26:22            10.1.0.82:54143         ESTABLISHED
tcp6       0      0 :::22                   :::*                    LISTEN     
tcp6       0      0 ::1:631                 :::*                    LISTEN     
tcp6       0      0 ::1:25                  :::*                    LISTEN     
[root@dashui ~]#

自定義函數

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

    }

#這裏自定義了一個函數,求出兩個變量的最大值。
[root@dashui ~]# awk -f awk.fun 
3
[root@dashui ~]# cat awk.fun 
function max(v1,v2) {
v1>v2?var=v1:var=v2
return var
}
BEGIN{a=3;b=2;print max(a,b)}

    注意:和以前的shell中的函數有些不一樣,這裏若是須要像awk函數中傳遞變量,則須要在括號中定義形參。


awk中調用shell命令

system命令

[root@dashui ~]# awk BEGIN'{system("hostname")}'
dashui.localdomain


空格是awk中的字符串鏈接符,若是system中須要使用awk中的變量可使用空格分隔,或者說除了awk的變量外其餘一概用""引用起來。

[root@dashui ~]# awk 'BEGIN{name="xiaoshui";system("echo your name is " name)}'
your name is xiaoshui


awk腳本

將awk程序寫成腳本,直接調用或執行

#將action寫入到腳本中,而後調用執行
[root@dashui ~]# awk -F: -f f1.awk /etc/passwd
root 0
bin 1
daemon 2
adm 3
hu 
[root@dashui ~]# cat f1.awk 
{if($3<4)print $1,$3}
#  腳本文件中的/bin/awk -f表示是用awk -f來執行下面語句
[root@dashui ~]# ./f1.awk -F: /etc/passwd
root 0
bin 1
daemon 2
adm 3
hu 
[root@dashui ~]# cat f1.awk 
#!/bin/awk -f
#this is a awk script
{if($3<4)print $1,$3}


向awk腳本傳遞參數

 格式:
    awkfile var=value var2=value2... Inputfile

# min和max分別對應腳本文件中的min和max
[root@dashui ~]# ./f1.awk -F: min=100 max=200 /etc/passwd
avahi-autoipd 170
abrt 173
rtkit 172
usbmuxd 113
pulse 171
[root@dashui ~]# cat f1.awk 
#!/bin/awk -f
{if($3>=min && $3<=max)print $1,$3}


強化練習

1.打印一個表頭,而且打印用戶名和ID

[root@dashui ~]# awk -F: 'BEGIN{printf "Username           UID\n"}{printf "%-20s %d\n",$1,$3}' /etc/passwd
Username           UID
root                 0
bin                  1
daemon               2
adm                  3
lp                   4
sync                 5
shutdown             6
halt                 7
mail                 8
operator             11
games                12

2.打印一個表頭和表尾,並打印用戶名,ID,及shell

[root@dashui ~]# awk -F: 'BEGIN{printf "Username           UID     Shell\n"}{printf "%-20s %d %10s\n",$1,$3,$5}END{print "Finished..."}' /etc/passwd

Username           UID     Shell
root                 0       root
bin                  1        bin
daemon               2     daemon
adm                  3        adm
lp                   4         lp
sync                 5       sync
shutdown             6   shutdown
...省略...
haha                 4376           
xixi                 4377           
hehe                 4378           
Finished...
[root@dashui ~]#

3.對文本中的字段進行字符個數統計

[root@dashui ~]# awk '{print $0,length($0)}' hah
nihao a haha 12
i amfanin 9
dashui 6

4.打印字符個數大於等於8的字段

#其實就是比題3多了一條if判斷語句
[root@dashui ~]# awk '{if(length($0)>8){print $0,length($0)}}' hah
nihao a haha 12
i amfanin 9

5.統計系統中shell的類型,並打印shell引用的次數

#中間運用了數組和for循環
[root@dashui ~]# awk -F: '{count[$7]++}END{for (i in count){print i,count[i]}}' /etc/passwd
/etc/nologin 2
/bin/sync 1
/bin/bash 47
/shell/csh 1
/sbin/nologin 34
/sbin/halt 1
/bin/csh 2
/sbin/shutdown 1

6.統計fstab文件中,各文件系統被引用的次數

#比上題多了一步地址定界
[root@dashui ~]# awk '/^[^#]/{count[$3]++}END{for (i in count){print i,count[i]}}' /etc/fstab 
swap 1
xfs 3

7.統計系統中各TCP鏈接狀態的數量

[root@dashui ~]# netstat -tan|awk '/^tcp\>/{state[$6]++}END{for (i in state){print i,state[i]}}'
LISTEN 3
ESTABLISHED 1

8.將文本的重複行去掉

#第一次讀取aaa,arr[aaa]由於沒有定義,因此爲空,又取非,因此爲真,arr[aaa]++是先賦值後加加,因此第一次打印出來
#第二次,在碰見aaa時候,由於以前有值了arr[aaa]=1,因此取反爲0,不執行後的print操做,可是++依然正常運行
[root@dashui ~]# awk '!arr[$0]++' hah
aaa
bbb
ccc
ddd
fff

[root@dashui ~]# cat hah 
aaa
bbb
ccc
aaa
ddd
aaa
aaa
fff
ccc

[root@dashui ~]#

                                                                                                         謝謝瀏覽,之後會不定時更新...

相關文章
相關標籤/搜索