AWK是一種優良的文本處理工具。其名稱得自于它的創始人阿爾佛雷德·艾侯、彼得·溫伯格和布萊恩·柯林漢姓氏的首個字母。AWK提供了極其強大的功能:可以進行正則表達式的匹配,樣式裝入、流控制、數學運算符、進程控制語句甚至于內置的變量和函數。它具備了一個完整的語言所應具有的幾乎所有精美特性。
AWK的基本用法
awk [options] ‘program’ var=value file…
awk [options] -f programfile var=value file…
awk [options] 'BEGIN{ action;… } pattern{ action;… } END{action;… }' file ...
awk 程序通常由: BEGIN語句塊、能夠使用模式匹配的通用語句塊、 END語句塊,共3部分組成
program通常是被單引號或雙引號中
基本格式:
awk [options] 'program' file…
program:pattern{action statements;..}
pattern和action:
pattern部分決定動作語句何時觸發及觸發事件( BEGIN,END)
action statements對數據進行處理,放在{}內指明( print, printf)
分割符、域和記錄:
awk執行時, 由分隔符分隔的字段(域)標記2..0為所有域,注意:和shell中變量0 的操作;
工作原理:
第一步:執行BEGIN{action;… }語句塊中的語句;
第二步:從文件或標準輸入(stdin)讀取一行,然后執行pattern{action;… }語句塊,它逐 行掃描文件,從第一行到最后一行重復這個過程,直到文件全部被讀取完畢;
第三步:當讀至輸入流末尾時,執行END{action;…}語句塊;
BEGIN語句塊在awk開始從輸入流中讀取行之前被執行,這是一個可選的語句塊,比如 變量初始化、打印輸出表格的表頭等語句通常可以寫在BEGIN語句塊中;
END語句塊在awk從輸入流中讀取完所有的行之后即被執行,比如打印所有行的分析結果這類信息匯總都是在END語句塊中完成,它也是一個可選語句塊;
pattern語句塊中的通用命令是最重要的部分,也是可選的。如果沒有提供pattern語句塊,則默認執行{ print },即打印每一個讀取到的行, awk讀取的每一行都會執行該語句塊;
常用選項
-F:指明輸入數據的字段分割符,默認為空白符;該選項的用法類似于命令cut的選項-f;
-v:自定義變量,用法為:-v var=VALUE;
變量
內建變量
FS:輸入數據字段分隔符,默認為空白符;
[root:~]# awk -v FS=":" '{print $1,$3}' /etc/passwd
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
##指明“:”作為字段分隔符,然后輸出第一字段和第三字段,輸出的字段默認分隔符為空白字符
OFS:指定輸出字段的分隔符,默認為空白字符;
[root:~]# awk -v FS=":" -v OFS="@@@@@@@@@@" '{print $1,$3}' /etc/passwd
root@@@@@@@@@@0
bin@@@@@@@@@@1
daemon@@@@@@@@@@2
adm@@@@@@@@@@3
lp@@@@@@@@@@4
sync@@@@@@@@@@5
shutdown@@@@@@@@@@6
##指定的輸出分隔符為“@@@@@@@@@”,所以輸出的兩個字段之間就不在是空白;
注意:在使用內置變量FS和OFS時,都需要用-v選項聲明。
RS:指定輸入時的換行符;
ORS:指定輸出是的換行符:
[root:~]# awk -v RS=":" -v ORS="#" '{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
##上面語句指定“:”為輸入時的行分隔符,而將輸出的行分隔符設定為“#”,所以輸出的字段由#分割的是行;
NF:每一行的字段數量,即每一行被默認的或者指定的分隔符分割成了多少個字段:
[root:~]# awk -v FS=":" '{print NF,$NF}' /etc/passwd
7 /bin/bash
7 /sbin/nologin
7 /sbin/nologin
7 /sbin/nologin
7 /sbin/nologin
7 /bin/sync
注意上例中,NF是7,代表分隔符“:”將文件中的每行分成了7可字段,而$NF則是分隔符將每行分成7個字段中的最后一個字段。
NR和FNR:輸出時顯示行號,它們之間的不同之處在于:在同時輸出多個文件時,NR會將所有文件的行號連續顯示,而FNR會單獨顯示每個文件的行號;
[root:~]# awk '{print NR,$0}' /etc/issue /etc/fstab
1 \S
2 Kernel \r on an \m
3
4 Mage Education Learning Services
5 http://www.magedu.com
6
7
8 #
9 # /etc/fstab
10 # Created by anaconda on Wed Nov 2 21:36:59 2016
11 #
12 # Accessible filesystems, by reference, are maintained under '/dev/disk'
13 # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
14 #
15 UUID=aff70d76-16d5-4559-97cb-9a83f2f638d3 / xfs defaults 0 0
16 UUID=8e197e5f-2a94-48c5-a5b6-044d83f8448d /boot xfs defaults 0 0
17 UUID=c378a443-213b-408a-975b-d87b8bc0b1a5 /usr xfs defaults 0 0
18 UUID=1e0def53-6464-422b-b713-9bf0d17faad0 swap swap defaults 0 0
##NR將兩個文件的行號連續輸出了
[root:~]# awk '{print FNR,$0}' /etc/issue /etc/fstab
1 \S
2 Kernel \r on an \m
3
4 Mage Education Learning Services
5 http://www.magedu.com
6
1
2 #
3 # /etc/fstab
4 # Created by anaconda on Wed Nov 2 21:36:59 2016
5 #
6 # Accessible filesystems, by reference, are maintained under '/dev/disk'
7 # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
8 #
9 UUID=aff70d76-16d5-4559-97cb-9a83f2f638d3 / xfs defaults 0 0
10 UUID=8e197e5f-2a94-48c5-a5b6-044d83f8448d /boot xfs defaults 0 0
11 UUID=c378a443-213b-408a-975b-d87b8bc0b1a5 /usr xfs defaults 0 0
12 UUID=1e0def53-6464-422b-b713-9bf0d17faad0 swap swap defaults 0 0
##FNR將每個文件的行號單獨顯示
ARGC:命令行參數的個數。這里值得注意的是:awk是命令行的參數;
ARGV:保存的是命令行給定的參數的數組,awk是命令行的第一個參數;
[root:~]# awk 'END{print ARGC}' /etc/issue /etc/fstab
3
[root:~]# awk 'END{print ARGV[0]}' /etc/issue /etc/fstab
awk
[root:~]# awk 'END{print ARGV[1]}' /etc/issue /etc/fstab
/etc/issue
[root:~]# awk 'END{print ARGV[2]}' /etc/issue /etc/fstab
/etc/fstab
[root:~]# awk 'END{print ARGV[3]}' /etc/issue /etc/fstab
[root:~]#
##ARGC將awk作為一個參數,所以顯示為3,而ARGV[0]顯示awk,則ARGV將awk作為了數組的第一個值。
自定義變量
自定義變量的格式為:-v VAR=VALVE
可以將變量定義在program外,也可以定義在program中,兩者不同之處是一個要用-v聲明,一個不用;
[root:~]# awk -v test="hello world" 'BEGIN{print test}' /etc/issue
hello world
[root:~]# awk 'BEGIN{test="hello world";print test}' /etc/issue
hello world
輸出打?。╬rint,printf)
print格式:print item1, item2, …
逗號分隔符,輸出的各item可以字符串,也可以是數值;當前記錄的字段、變量或awk的表達式,如省略item,相當于print $0;
[root:~]# awk '/^UUID/{print $1"\t"$3}' /etc/fstab
UUID=aff70d76-16d5-4559-97cb-9a83f2f638d3 xfs
UUID=8e197e5f-2a94-48c5-a5b6-044d83f8448d xfs
UUID=c378a443-213b-408a-975b-d87b8bc0b1a5 xfs
UUID=1e0def53-6464-422b-b713-9bf0d17faad0 swap
[root:~]# awk '/^UUID/{print}' /etc/fstab
UUID=aff70d76-16d5-4559-97cb-9a83f2f638d3 / xfs defaults 0 0
UUID=8e197e5f-2a94-48c5-a5b6-044d83f8448d /boot xfs defaults 0 0
UUID=c378a443-213b-408a-975b-d87b8bc0b1a5 /usr xfs defaults 0 0
UUID=1e0def53-6464-422b-b713-9bf0d17faad0 swap swap defaults 0 0
printf格式:printf FORMAT, item1, item2, …
注意FORMAT必須給出,在顯示多行時,不會自動換行,要給定換行符“\n”;并且需要分別對后面要輸出的每個item指定輸出格式;
格式符
%c: 顯示字符的ASCII碼
%d, %i: 顯示十進制整數
%e, %E:顯示科學計數法數值
%f:顯示為浮點數
%g, %G:以科學計數法或浮點形式顯示數值
%s:顯示字符串
%u:無符號整數
%%: 顯示%自身
修飾符
#[.#]:第一個數字控制顯示的寬度;第二個#表示小數點后精度, %3.1f
-: 左對齊(默認右對齊) %-15s
+:顯示數值的正負符號 %+d
[root:~]# awk -v FS=":" '{printf "username:%-20s UID:%5.2f\n",$1,$3}' /etc/passwd
username:root UID: 0.00
username:bin UID: 1.00
.
.
username:nfsnobody UID:65534.00
username:postfix UID:89.00
username:sshd UID:74.00
username:ntp UID:38.00
username:tcpdump UID:72.00
## "username:%-20s UID:%5.2f\n"中的%-20s是左對齊20個字符顯示字符串,%5.2f是以浮點數形式顯示后面的UID數值。
操作符
awk類似shell語言,也有類似shell的操作符,如算術操作符、賦值操作符、比較操作符、模式匹配操作符等等,下面列出了常用的操作符:
操作符類型 | 操作符 |
---|---|
算術操作符 | +,-,*,/,^,%,-x,+x |
比較操作符 | <,<=,>,>=,==,!= |
賦值操作符 | =,+=,-=,*=,/=,%=,^=,++,– |
模式匹配操作符 | ~,!~ |
邏輯操作符 | &&,!,II |
函數調用:function_name(argu1,argu2,…)
條件表達式(三目表達式):selector?if-true-expression:if-false-expression
[root:~]# awk -F: '{$3>=1000?tpye="common user":tpye="sys user";printf "%-30s %d\n",tpye":"$1,$3}' /etc/passwd
sys user:root 0
sys user:bin 1
sys user:daemon 2
sys user:adm 3
sys user:lp 4
sys user:sync 5
sys user:shutdown 6
sys user:halt 7
sys user:mail 8
sys user:operator 11
sys user:games 12
sys user:ftp 14
sys user:nobody 99
sys user:avahi-autoipd 170
sys user:systemd-bus-proxy 999
sys user:systemd-network 998
sys user:dbus 81
sys user:polkitd 997
sys user:abrt 173
sys user:colord 996
sys user:libstoragemgmt 995
sys user:setroubleshoot 994
sys user:rpc 32
sys user:rtkit 172
sys user:chrony 993
sys user:tss 59
sys user:geoclue 992
sys user:usbmuxd 113
sys user:mysql 27
sys user:pulse 171
sys user:gdm 42
sys user:rpcuser 29
common user:nfsnobody 65534
sys user:postfix 89
sys user:sshd 74
sys user:ntp 38
sys user:tcpdump 72
common user:hacker 1001
PATTERN
awk中可以根據pattern過濾行:
空模式(empty),匹配每一行
[root:~]# awk '{print}' /etc/issue
\S
Kernel \r on an \m
/RGEXP/:僅處理匹配到的行
[root:~]# awk '/^#/{print}' /etc/fstab
#
# /etc/fstab
# Created by anaconda on Wed Nov 2 21:36:59 2016
#
# 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
#
關系表達式:結果為真則處理,結果不為真則不處理
[root:~]# df | awk '$1~"/dev/"{print}'
/dev/sda2 41922560 1556776 40365784 4% /
/dev/sda3 20961280 12422564 8538716 60% /usr
/dev/sda1 508588 193720 314868 39% /boot
地址定界:僅處匹配到的范圍行
[root:~]# awk '/^root/,/^ftp/{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
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
###從匹配到以root開頭的行開始,到匹配到以ftp開頭的行結束
BEGIN/END模式:
BEGIN{}:僅在開始處理文件之前執行一次
END{}:僅在文本處理完成之后執行一次
[root:~]# df |awk 'BEGIN{printf "%-10s%-4s\n%s\n","DISK","USED","----------------"}/^\/dev/{print $1,$5}END{print "================\n","end\n"}'
DISK USED
----------------
/dev/sda2 4%
/dev/sda3 60%
/dev/sda1 39%
================
end
action
常用的action有:
表達式:如算術表達式,比較表達式;
控制語句:和shell相似的if、while、for等控制語句;
組合語句:多條控制語句組合在一起;
輸入、輸出語句:如print、printf語句;
awk的使用中,經常能看見很多用法和shell語言相似的地方;一樣有if語句,有while語句,for循環語句等。它們的使用是awk工具更好用,功能更強大。
if語句語法:if(condition){statement}[else{statement}]。舉例:
## if
[root:~]# awk -F: '{if($3>=1000)printf "%-15s%d\n",$1,$3}' /etc/passwd
nfsnobody 65534
hacker 1001
## if-else
[root:~]# awk -F: '{if($3>=1000)printf "%-15s%d\n",$1,$3;else print "Sys User:",$1}' /etc/passwd
Sys User: root
Sys User: bin
.
.
Sys User: gdm
Sys User: rpcuser
nfsnobody 65534
Sys User: postfix
Sys User: sshd
Sys User: ntp
Sys User: tcpdump
hacker 1001
while語句語法:while(condition){statement},當條件為非0或者非空字符串時為真,執行后面語句;
使用場景:對一行內的多個字段逐一處理時使用;對數組內的元素逐一處理時使用。
[root:~]# awk -F: '{i=0;while(i<=NF){if(length($i)>100)printf "user:%s have passwd: %s\n",$1,$2;i++}}' /etc/shadow
user:root have passwd: $6$tR88V6XX$Dmy2cI.zo5UT.ndn0uoufO0cCfOssRIhs/OJcPA58SQcykUGkC83RnI4sbrjXaWQ71CTVx4xslsskL2jf6r2A0
user:hacker have passwd: $6$zK50RPcQ$JADFdJDBJQ8f1RzT5LzxAIbE/pLtcdyYR9Cw2ZneebLEUgq3ltyPk0fJ0FM9hhZl3P2K8ak0b3vZEeOOO6az11
do-while語法:do{statement}while(condition),該語句和while語句的不同之處就是不管條件是否為真,都執行一次語句在判斷條件是否為真。
for語句的語法:for(expr1;expr2;expr3){statement},此處的for用戶和shell語句中的for特殊用法一樣。
[root:~]# awk 'BEGIN{for(i=1;i<=100;i++)sum+=i;print sum}'
5050
注意:在awk中,for循環語句也有特殊用法,它可以遍歷數組中的元素,這個特殊用法我們在下面數組中演示。
switch語句語法:switch(condition){case VALUE1 or REGEXP1:statement1;case VALUE2 or REGEXP2:statement;…;default:statement}
注意:在awk的循環語句中,同樣支持continue、break語法,用法和shell語句中的一樣。不僅如此,awk還支持next語法,用法和continue、break一樣,不過它的作用是跳出當前行循環,進行下一樣的處理,例如:
[root:~]# awk -F: '{if($3%2==1)next;print NR,$0}' /etc/passwd
1 root:x:0:0:root:/root:/bin/bash
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
11 games:x:12:100:games:/usr/games:/sbin/nologin
12 ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
14 avahi-autoipd:x:170:170:Avahi IPv4LL Stack:/var/lib/avahi-autoipd:/sbin/nologin
16 systemd-network:x:998:996:systemd Network Management:/:/sbin/nologin
20 colord:x:996:994:User for colord:/var/lib/colord:/sbin/nologin
22 setroubleshoot:x:994:991:setroubleshoot,,62985600:/var/lib/setroubleshoot:/sbin/nologin
23 rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin
24 rtkit:x:172:172:RealtimeKit:/proc:/sbin/nologin
27 geoclue:x:992:989:User for geoclue:/var/lib/geoclue:/sbin/nologin
31 gdm:x:42:42:gdm,,62985600:/var/lib/gdm:/sbin/nologin
33 nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
35 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
36 ntp:x:38:38:ntp,,62985600:/etc/ntp:/sbin/nologin
37 tcpdump:x:72:72:tcpdump,,62985600:/:/sbin/nologin
數組
關聯數組: array[index-expression]
index-expression:
(1) 可使用任意字符串;字符串要使用雙引號括起來;
(2) 如果某數組元素事先不存在,在引用時, awk會自動創建此元素,并將其值初始化為“空串”;若要判斷數組中是否存在某元素,要使用“index in array”格式進行遍歷(index是關鍵字,就不要在awk腳本中使用);
如統計某時刻,網絡監聽情況,就可以是使用數組遍歷的方式來統計:
root:~]# netstat -nta | awk '/^tcp\>/{print $4}' | awk -F: '{IP[$1]++}END{for(i in IP){print i,IP[i]}}'
127.0.0.1 2
0.0.0.0 1
172.16.252.5 1
## IP[$1]++將ip地址作為數組ip的索引號,來初始化數組元素,當相同的ip出現,則將數組元素的值加1,這樣,就把ip出現的次數統計成數組元素,輸出數組索引號和數組元素就把ip以及出現的次數統計出來了。
例如:統計/etc/fstab文件中每個文件系統類型出現的次數
[root:~]# awk '!/^#/&&!/^$/{count[$3]++}END{for(i in count)print i,count[i]}' /etc/fstab
swap 1
xfs 3
內置函數
函數rand():
返回一個0~1之間的一個隨機數,需要配合函數srand()一起使用:
[root:~]# awk 'BEGIN{srand();print rand()}'
0.350256
[root:~]# awk 'BEGIN{srand();print rand()}'
0.608501
[root:~]# awk 'BEGIN{srand();print rand()}'
0.811887
[root:~]# awk 'BEGIN{srand();print rand()}'
0.676783
[root:~]# awk 'BEGIN{srand();print rand()}'
0.676783
[root:~]# awk 'BEGIN{srand();print rand()}'
0.0149217
[root:~]# awk 'BEGIN{srand();print rand()}'
0.804012
##如果沒有函數srand(),返回的值不變:
[root:~]# awk 'BEGIN{print rand()}'
0.237788
[root:~]# awk 'BEGIN{print rand()}'
0.237788
[root:~]# awk 'BEGIN{print rand()}'
0.237788
函數length()
返回一個字符串的長度:
[root:~]# awk -F: '{if($2!="*"&&$2!="!!")print $1,length($2)}' /etc/shadow
root 98
hacker 98
函數int()
取值整數:
[root:~]# awk 'BEGIN{srand();print int(rand()*100)}'
69
[root:~]# awk 'BEGIN{srand();print int(rand()*100)}'
43
[root:~]# awk 'BEGIN{srand();print int(rand()*100)}'
2
[root:~]# awk 'BEGIN{srand();print int(rand()*100)}'
93
[root:~]# awk 'BEGIN{srand();print int(rand()*100)}'
93
[root:~]# awk 'BEGIN{srand();print int(rand()*100)}'
93
函數sub(r,s,[t])、gsub(s,r,[t])
sub函數以r表示的模式來查找t所表示的字符串中的匹配的內容,并將其第一次出現替換為s所表示的內容;gsub函數以r表示的模式來查找t所表示的字符串中的匹配的內容,并將所有能匹配到的替換為s所表示的內容。
[root:~]# awk 'sub(/root/,"rooter",$0)' /etc/passwd
rooter:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/rooter:/sbin/nologin
[root:~]# awk 'gsub(/root/,"rooter",$0)' /etc/passwd
rooter:x:0:0:rooter:/rooter:/bin/bash
operator:x:11:0:operator:/rooter:/sbin/nologin
函數split(s,array,[r])
以r為分隔符,切割字符s,并將切割后的結果保存至array所表示的數組中,第一個索引值為1,第二個索引值為2,…
[root:~]# cat /var/log/httpd/access_log | awk '{split($0,IP," ");count[IP[1]]++}END{for(i in count)print i,count[i]}'
127.0.0.1 2
172.16.250.92 4
自定義函數
格式:
function name ( parameter, parameter, … ) {
statements
return expression
}
parameter是自定義函數的形式參數,是指定函數需要一個參數的表示,在真正使用函數的過程中,可以根據實際要求定義。
示例:
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語句中點用shell命令
使用system引用shell命令;空格是awk中的字符串連接符,如果system中需要使用awk中的變量可以使用空格分隔,或者說除了awk的變量外其他一律用”“引用起來。
在awk中語句中,可以使用shell命令,如下:
[root:~]# awk 'BEGIN{system("uname -r")}'
2.6.32-642.el6.x86_64
#########################################
[root:~]# awk 'BEGIN{system("[ 3 > 2 ] && echo right")}'
right
awk腳本
將awk程序寫成腳本,直接調用或執行
示例:
cat f1.awk
if($3>=1000)print $1,$3}
#awk -F: -f f1.awk /etc/passwd
#cat f2.awk
#!/bin/awk –f
#this is a awk script
{if($3>=1000)print $1,$3}
#chmod +x f2.awk
#f2.awk –F: /etc/passwd
向awk腳本傳遞參數
格式:awkfile var=value var2=value2… Inputfile
示例:
#cat test.awk
#!/bin/awk –f
{if($3 >=min && $3<=max)print $1,$3}
#chmod +x test.awk
#test.awk -F: min=100 max=200 /etc/passwd
原創文章,作者:王更生,如若轉載,請注明出處:http://www.www58058.com/62146