Shell三劍客之a(chǎn)wk
1. awk概述
1.1 awk是什么
awk不同于grep的文本搜索與sed工具的文本處理,它更偏向于對(duì)文本的格式化處理輸出,它不僅僅是一款工具,也是一門(mén)解釋性語(yǔ)言,其名字來(lái)源于它的三位作者的姓氏:Alfred Aho, Peter Weinberger 和 Brian Kernighan,在文本處理功能非常強(qiáng)大,是一款Linux服務(wù)器文本報(bào)告生成器和格式化文本輸出工具。
1.2 為什么用awk
我們?nèi)粘9ぷ髦杏泻芏嘈枰袷交蛴≥敵龅男枨?,更多的是關(guān)注列操作時(shí),就可以利用awk工具來(lái)進(jìn)行處理。awk除了是工具也同樣是一門(mén)語(yǔ)言,其允許用戶(hù)創(chuàng)建簡(jiǎn)短的程序來(lái)處理自己的需求,這些程序讀取輸入文件、為數(shù)據(jù)排序、處理數(shù)據(jù)、對(duì)輸入執(zhí)行計(jì)算以及生成報(bào)表等。功能非常的強(qiáng)大,相信在掌握了awk,日常運(yùn)維工作更加方便高效簡(jiǎn)單。
2. awk的適用場(chǎng)景
- 超大文件處理;
- 輸出格式化的文本報(bào)表;
- 執(zhí)行算數(shù)運(yùn)算;
- 執(zhí)行字符串操作等。
3. awk的處理模式
一般是遍歷一個(gè)文件中的每一行,然后分別對(duì)文件的每一行進(jìn)行處理。
awk對(duì)輸入的一行數(shù)據(jù)進(jìn)行處理的模式,對(duì)整個(gè)文件進(jìn)行重復(fù)執(zhí)行此模式處理,在此說(shuō)明對(duì)輸入的一行數(shù)據(jù)處理的內(nèi)在機(jī)制如下圖所示:

處理過(guò)程不斷重復(fù),直到到達(dá)文件結(jié)尾。
- 首先讀入文件流的一行到模式空間;
- 在模式空間內(nèi),對(duì)內(nèi)容進(jìn)行模式匹配處理;
- 然后輸出處理后的數(shù)據(jù)內(nèi)容;
- 清空當(dāng)前模式空間;
- 讀取第二行輸入流到模式空間;
- 又開(kāi)始對(duì)模式空間內(nèi)的第二行輸入數(shù)據(jù)進(jìn)行處理。
總體可以分為以下三步:
-
讀(Read):AWK 從輸入流(文件、管道或者標(biāo)準(zhǔn)輸入)中讀入一行然后將其存入內(nèi)存中。
-
執(zhí)行(Execute):對(duì)于每一行輸入,所有的 AWK 命令按順序執(zhí)行。 默認(rèn)情況下,AWK 命令是針對(duì)于每一行輸入,但是我們可以將其限制在指定的模式中。
-
重復(fù)(Repeate):一直重復(fù)上述兩個(gè)過(guò)程直到文件結(jié)束。
4. 語(yǔ)法及結(jié)構(gòu)
4.1 語(yǔ)法
Awk 語(yǔ)法格式如下圖所示:

awk [options] 'PATTERN {action}' file1,file2
awk 的語(yǔ)法格式主要分為四個(gè)字段,options 選項(xiàng),引號(hào)內(nèi)有模塊與動(dòng)作,以及要處理的文件,接下來(lái)讓我們?cè)敿?xì)講解每一個(gè)語(yǔ)法字段,更全面地認(rèn)識(shí) awk 這個(gè)腳本利器。
4.2 程序結(jié)構(gòu)
awk 在引號(hào)內(nèi)有一定的程序結(jié)構(gòu),主要為以下:
- 開(kāi)始?jí)K(BEGIN BLOCK):
語(yǔ)法:
BEGIN{awk-commands}
開(kāi)始?jí)K就是awk程序啟動(dòng)時(shí)執(zhí)行的代碼部分(在處理輸入流之前執(zhí)行),并且在整個(gè)過(guò)程中只執(zhí)行一次;
一般情況下,我們?cè)陂_(kāi)始?jí)K中初始化一些變量。BEGIN是awk的關(guān)鍵字,因此必須要大寫(xiě)?!咀ⅲ洪_(kāi)始?jí)K部分是可選,即你的awk程序可以沒(méi)有開(kāi)始?jí)K部分】
- 主體塊(Body Block):
語(yǔ)法:
/pattern/{awk-commands}
針對(duì)每一個(gè)輸入的行都會(huì)執(zhí)行一次主體部分的命令,默認(rèn)情況下,對(duì)于輸入的每一行,awk都會(huì)執(zhí)行主體部分的命令,但是我們可以使用/pattern/限制其在指定模式下。
- 結(jié)束塊(END BLOCK):
語(yǔ)法:
END{awk-commands}
結(jié)束塊是awk程序結(jié)束時(shí)執(zhí)行的代碼(在處理完輸入流之后執(zhí)行),END也是awk的關(guān)鍵字,必須大寫(xiě),與開(kāi)始?jí)K類(lèi)似,結(jié)束塊也是可選的。
4.3 awk命令詳解
4.3.1 awk 輸出
- awk print輸出,例如:
print item1,item2...
1.各字段之間逗號(hào)隔開(kāi),輸出時(shí)以空白字符分隔;
2.輸出的字段可以為字符串或數(shù)值,當(dāng)前記錄的字段(如$1)、變量或 awk 的表達(dá)式;數(shù)值先會(huì)轉(zhuǎn)換成字符串然后輸出;
3.print 命令后面的 item 可以省略,此時(shí)其功能相當(dāng)于print $0
,如果想輸出空白,可以使用print ""
;
例如:
[root@master ~]# awk -F: '{print $1,$NF}' /etc/passwd|column -t
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
adm /sbin/nologin
lp /sbin/nologin
sync /bin/sync
- awk printf 輸出
printf 命令的使用格式:
printf <format> item1,item2...
要點(diǎn):
1.其與 print 命令最大區(qū)別,printf 需要指定 format,format 必須給出;
2.format 用于指定后面的每個(gè) item 輸出格式;
3.printf 語(yǔ)句不會(huì)自動(dòng)打印換行字符\n
。
format 格式的指示符都以 % 開(kāi)頭,后跟一個(gè)字符:
%c:顯示ascall碼
%d:%i:十進(jìn)制整數(shù)
%e,%E:科學(xué)計(jì)數(shù)法
%f:浮點(diǎn)數(shù)
%s:字符串
%u:無(wú)符號(hào)整數(shù)
%%:顯示%自身
修飾符:
#[.#]:第一個(gè)#控制顯示的寬度:第二個(gè)#表示小數(shù)點(diǎn)后的精度:
%3.1f
-:左對(duì)齊
+:顯示數(shù)組符號(hào)
例如:
[root@master ~]# awk -F: '{printf "Username:%-15s ,Uid:%d\n",$1,$3}' /etc/passwd
Username:root ,Uid:0
Username:bin ,Uid:1
Username:daemon ,Uid:2
Username:adm ,Uid:3
Username:lp ,Uid:4
Username:sync ,Uid:5
Username:shutdown ,Uid:6
4.3.2 awk變量
-
記錄變量:
- IFS(input field separator),輸入字段分隔符(默認(rèn)空白)
- OFS(output field separator),輸出字段分隔符
- RS(Record separator):輸入文本換行符(默認(rèn)回車(chē))
- ORS:輸出文本換行符
-
數(shù)據(jù)變量
- NR:the number of input records,awk 命令所處理的文件的行數(shù),如果有多個(gè)文件,這個(gè)數(shù)目會(huì)將處理的多個(gè)文件計(jì)數(shù)
- NF:number of field,當(dāng)前記錄的 field 個(gè)數(shù)
{print NF},{print $NF}
-
ARGV:數(shù)組,保存命令行本身這個(gè)字符串
-
ARGC:awk 命令的參數(shù)個(gè)數(shù)
-
FILENAME:awk 命令處理的文件名稱(chēng)
-
ENVIRON:當(dāng)前 shell 環(huán)境變量及其值的關(guān)聯(lián)數(shù)組
awk 'BEGIN{print ENVIRON["PATH"]}'
-
自定義變量
-v var=value
變量名區(qū)分大小寫(xiě),例如:
[root@master ~]# awk -v test="abc" 'BEGIN{print test}' abc [root@master ~]# awk 'BEGIN{var="name";print var}' name
4.3.3 操作符
-
算術(shù)運(yùn)算
- +,-,*,/,^,%。例如:
[root@master ~]# awk 'BEGIN{a=5;b=3;print "a + b =",a+b}' a + b = 8
-
字符串操作
- 無(wú)符號(hào)操作符,表示字符串連接,例如:
[root@master ~]# awk 'BEGIN { str1="Hello,"; str2="World"; str3 = str1 str2; print str3 }' Hello,World
-
賦值操作符:
- =,+=,-=,*=,/=,%=,^=,例如:
[root@master ~]# awk 'BEGIN{a=5;b=6;if(a == b) print "a == b";else print "a!=b"}' a!=b [root@master ~]# awk -F: '{sum+=$3}END{print sum}' /etc/passwd 72349
-
比較操作符:
- >,>=,<,<=,!=,==
-
模式匹配符:
- ~:是否匹配
- !~:是否不匹配
例如:
[root@master ~]# awk -F: '$1~"root"{print $0}' /etc/passwd root:x:0:0:root:/root:/bin/bash
-
邏輯操作符:
- && 、 || 、 !,例如:
[root@master ~]# awk 'BEGIN{a=6;if(a > 0 && a <= 6) print "true";else print "false"}' true
-
函數(shù)調(diào)用:
- function_name(argu1,augu2)
-
條件表達(dá)式(三元運(yùn)算):
- selection?if-true-expresssion:if-false-expression
[root@master ~]# awk -F: '{$3>=100?usertype="common user":usertype="sysadmin";printf "%15s:%s\n",$1,usertype}' /etc/passwd root:sysadmin bin:sysadmin daemon:sysadmin adm:sysadmin lp:sysadmin sync:sysadmin shutdown:sysadmin halt:sysadmin
4.3.4 Pattern
- empty:空模式,匹配每一行
- /regular expression/:僅處理能被此處模式匹配到的行,例如;
[root@master ~]# awk -F: '$NF=="/bin/bash"{printf "%15s,%s\n",$NF,$1}' /etc/passwd
/bin/bash,root
- relational expression:關(guān)系表達(dá)式,結(jié)果為“真”有“假”,結(jié)果為“真”才會(huì)被處理。
Tips:使用模式需要使用雙斜線(xiàn)括起來(lái),真:結(jié)果為非0值,非空字符串。
[root@master ~]# awk -F: '$3>100{print $1,$3}' /etc/passwd
systemd-network 192
polkitd 999
ceph 167
kube 998
etcd 997
gluster 996
nfsnobody 65534
chrony 995
redis 994
awk -F: '$NF=="/bin/bash"{printf "%15s,%s\n",$NF,$1}' /etc/passwd
awk -F: '$NF~/bash$/{printf "%15s,%s\n",$NF,$1}' /etc/passwd
df -Th|awk '/^\/dev/{print}'
-
line ranges:行范圍,制定startline,endline。
[root@master ~]# awk -F: '/10/,/20/{print $1}' /etc/passwd games ftp nobody systemd-network dbus polkitd postfix sshd ceph kube etcd gluster rpc
-
BEGIN/END模式
- BEGIN{}:僅在開(kāi)始處理文本之前執(zhí)行一次
- END{}:僅在文本處理完成之后執(zhí)行一次
[root@master ~]# awk -F: 'BEGIN{print "username uid\n--------------------"}{printf "%-15s:%d\n",$1,$3}END{print "-----------------\nend"}' /etc/passwd
username uid
--------------------
root :0
bin :1
daemon :2
adm :3
lp :4
rpc :32
rpcuser :29
nfsnobody :65534
chrony :995
redis :994
-----------------
end
4.3.5 控制語(yǔ)句
- if(condition) {statements},例如:
[root@master ~]# awk -F: '{if($3>100) print $1,$3}' /etc/passwd
systemd-network 192
polkitd 999
ceph 167
kube 998
etcd 997
gluster 996
nfsnobody 65534
chrony 995
redis 994
- if(condition) {statments} [else {statments}],例如:
[root@master ~]# awk -F: '{if($3>100) {printf "Common user:%-15s\n",$1} else {printf "sysadmin user:%-15s\n",$1}}' /etc/passwd
sysadmin user:root
sysadmin user:bin
sysadmin user:daemon
sysadmin user:adm
sysadmin user:lp
sysadmin user:sync
sysadmin user:shutdown
sysadmin user:halt
sysadmin user:mail
sysadmin user:operator
sysadmin user:games
5. 實(shí)例
1.統(tǒng)計(jì)/etc/fstab文件中每個(gè)單詞出現(xiàn)的次數(shù),并按從大到小排序
awk '{for(i=1;i<=NF;i++){words[$i]++}}END{for(key in words)print key,words[key]}' /etc/fstab|sort -k2 -nr
awk '{ips[$1]++}END{for(i in ips) print i,ips[i]}' access_nginx.log |column -t|sort -k2 -nr
2.統(tǒng)計(jì)/etc/fstab每個(gè)文件系統(tǒng)類(lèi)型出現(xiàn)的次數(shù)
awk '!/^#/&&!/^$/{dev[$3]++}END{for(i in dev) print i,dev[i]}' /etc/fstab
3.ping一個(gè)域名,輸出ping此刻的時(shí)間
ping baidu.com|awk '{print $0" "strftime("%Y-%m-%d %H:%M:%S")}'
4.利用netstat監(jiān)控服務(wù)是否正常監(jiān)聽(tīng)
netstat -lntup|awk 'NR>2{if($4 ~/.*:22/) print $0"yes";exit 0}'
5.統(tǒng)計(jì)web服務(wù)器日志狀態(tài)碼
awk '$9~"[0-9]"{stat[$9]++}END{for(i in stat) print i,stat[i]}' access_log
6. 注意事項(xiàng)
-
awk
同sed
命令類(lèi)似,只不過(guò)sed
擅長(zhǎng)取行,awk
命令擅長(zhǎng)取列,awk是對(duì)文本進(jìn)行格式化輸出,sed更傾向于對(duì)文件進(jìn)行修改; -
對(duì)于讀入的文件可以根據(jù)自己需求對(duì)IFS/OFS對(duì)輸入和輸出進(jìn)行修改;
-
awk非常的強(qiáng)大,但是也是三劍客中最難的一個(gè),其作為一門(mén)單獨(dú)的語(yǔ)言,我們?cè)赟hell編程中學(xué)習(xí)常用的命令及語(yǔ)法就已經(jīng)足夠我們使用。
7. 小結(jié)
本章節(jié)我們系統(tǒng)性地學(xué)習(xí)了awk的語(yǔ)法結(jié)構(gòu)及處理模式,其相較于其他文本處理工具,更適合對(duì)文本進(jìn)行格式化輸出,我們需要在合適的地方使用,其作為L(zhǎng)inux系統(tǒng)上一個(gè)非常強(qiáng)大的文本格式輸出工具,也是一門(mén)語(yǔ)言,后期需要在實(shí)踐工作中更多地靈活運(yùn)用,使得腳本編寫(xiě)更加方便。