在管理和維護Linux系統過程當中,有時可能須要從一個具備必定格式的文本(格式化文本)中抽取數據,這時可使用awk編輯器來完成這項任務。發明這個工具的做者是Aho、Weinberg和Kernighan,取三我的名的首字母而得名awk。正則表達式
與sed相比,awk更擅長處理格式化文本。格式化文本通常使用某個特定的字符(稱爲域分隔符)將文本中不一樣的字段(稱爲域)隔開。例如用於保存用戶信息的系統用戶文件/etc/passwd,該文件使用冒號分別將用戶名、密碼、UID等字段分隔開。shell
awk [-F] ‘command’ input-file數組
awk –f script input-filebash
與sed相似,awk也有兩種調用方式:第一種是直接使用awk命令調用,選項F用於指定域分隔符。默認狀況下awk使用的域分隔符是空格,若是要處理的文件input-file的域分隔符不是空格,應該使用F選項另行指定。第二種方法跟sed同樣,先將要輸入的選項模式和動做放入一個腳本文件中,而後使用選項f調用。服務器
awk被調用後,首先讀入第一行文本並按選項F指定的域分隔符將各個字段劃開。以/etc/passwd其中一行爲例:網絡
root:x:0:0:root:/root:/bin/bash
處理這個文件時,應該使用選項F指定域分隔符爲冒號「:」,劃分完成後將這一行稱爲一條記錄。一條記錄中的各個字段按順序稱爲域1,域2,域3……爲方便對這些字段進行處理,使用標識符「$1」表示第一個字段,「$2」表示第二個字段…依次類推。若是要表示整條記錄,應該使用標識符「$0」。編輯器
劃分記錄後,awk會按預約的模式和動做處理記錄。處理完一條記錄後,又會讀取文本第二行重複上述動做。函數
一個完整的awk命令由編輯語句和格式化文本組成。編輯語句由一個或多個模式和動做組成,格式化文本即用戶須要處理的文本,能夠來自文件,也能夠來自命令輸出。工具
與sed同樣,模式用來指定動做執行的文本位置。在awk中,模式能夠是條件語句、模式匹配、正則表達式等。若是沒有指定模式,awk會對全部記錄執行編輯語句。測試
動做通常放在模式後面的大括號{}內,通常是awk的內置函數,例如輸出函數print等。
下面是一個輸出文件/etc/passwd中全部用戶名的示例:
[root@localhost test]# awk -F: '{print "username:" $1}' /etc/passwd username:root username:bin username:daemon username:adm username:lp ...
上面這個示例命令使用選項F指定域分隔符爲冒號,後面的命令中並無指定模式,awk將默認匹配全部行。放在大括號內的動做使用了awk的內置函數print,先輸出了一個字符串「username:」,而後使用標識符「$1」輸出第一個字段。
在awk中,有兩個特殊的表達式BEGIN和END。BEGIN語句將在編輯語句開始執行以前運行,一般用來輸出文本頭信息。END語句則在全部編輯語句完成以後執行,通常用來輸出結束信息和統計數據等。
使用示例文件students(見查找文本工具grep),製做一個輸出學生學號和姓名,並輸出文本頭、文本尾的例子:
[root@localhost test]# awk 'BEGIN{print "Student ID name\n-----------------------"}{print $1"\t"$2}END{print "------------END-----------"}' students Student ID name ----------------------- 2821020225 Liulu 2821020115 Liumin 2721020321 Xuli 2921020632 Xiayu 2721010409 Liwei 2921050313 Heli 2721030227 Wangtao ------------END-----------
在awk中,除了可使用正則表達式和元字符外,awk還內置了算術運算符、操做符。
=(賦值)、+(加)、-(減)、-(負)、*(乘)、/(除)、前置++、後置++、前置--、後置--、?:(條件)、%(求模)、+=(複合加)、-=(複合減)、*=(複合乘)、/=(複合除)、%=(複合求模)、^(冪)、^=(複合冪)
>(大於)、>=(大於等於)、<(小於)、<=(小於等於)、!=(不等於)、==(等於)
~/pattern/(匹配pattern)、!~/pattern/(不匹配pattern)
||(或)、&&(與)、!(非)
用法示例:
(1)結合正則表達式,輸出全部2007年入學的學生信息:
[root@localhost test]# awk '/27210/{print $0}' students 2721020321 Xuli Jiangsu Luolei 12/25/92 76 81 85 79 321 80 2721010409 Liwei Sichuan tangwei 11/21/92 98 88 85 85 356 89 2721030227 Wangtao Yunnan Huli 03/21/93 87 76 69 88 320 80
(2)結合正則表達式,輸出全部來自Sichuan的學生學號、姓名和總分:
[root@localhost test]# awk '/Sichuan/{print $1"\t"$2"\t"$10}' students 2821020225 Liulu 325 2721010409 Liwei 356
(3)能夠指定某個域的取值精確匹配。例如要輸出姓名爲Liwei的學生信息:
[root@localhost test]# awk '$2=="Liwei"{print $0}' students 2721010409 Liwei Sichuan tangwei 11/21/92 98 88 85 85 356 89
(4)精確匹配時,可使用「!=」表示不匹配。例如要輸出不是來自Sichuan的學生信息:
[root@localhost test]# awk '$3!="Sichuan"{print $0}' students 2821020115 Liumin Henan lixia 05/14/94 78 65 59 78 280 70 2721020321 Xuli Jiangsu Luolei 12/25/92 76 81 85 79 321 80 2921020632 Xiayu Shanxi Hetao 03/26/93 78 86 92 78 334 84 2921050313 Heli Xizang Tangwei 07/12/94 56 78 80 45 259 65 2721030227 Wangtao Yunnan Huli 03/21/93 87 76 69 88 320 80
(5)也可使用操做符表示匹配,例如要輸出全部輔導員不是Tangwei的學生的信息:
[root@localhost test]# awk '$4 !~/[Tt]angwei/{print $0}' students 2821020225 Liulu Sichuan Lixia 01/23/93 89 76 88 72 325 81 2821020115 Liumin Henan lixia 05/14/94 78 65 59 78 280 70 2721020321 Xuli Jiangsu Luolei 12/25/92 76 81 85 79 321 80 2921020632 Xiayu Shanxi Hetao 03/26/93 78 86 92 78 334 84 2721030227 Wangtao Yunnan Huli 03/21/93 87 76 69 88 320 80
(6)也能夠將匹配操做符與正則表達式一塊兒使用。例如輸出全部93年之後出生的學生信息:
[root@localhost test]# awk '$5 ~/^.......[3-9]/{print $0}' students 2821020225 Liulu Sichuan Lixia 01/23/93 89 76 88 72 325 81 2821020115 Liumin Henan lixia 05/14/94 78 65 59 78 280 70 2921020632 Xiayu Shanxi Hetao 03/26/93 78 86 92 78 334 84 2921050313 Heli Xizang Tangwei 07/12/94 56 78 80 45 259 65 2721030227 Wangtao Yunnan Huli 03/21/93 87 76 69 88 320 80
(7)使用豎線「|」匹配兩邊模式之一,例如要輸出全部來自Sichuan和Yunnan的學生信息:
[root@localhost test]# awk '$3 ~/(Sichuan|Yunnan)/{print $0}' students 2821020225 Liulu Sichuan Lixia 01/23/93 89 76 88 72 325 81 2721010409 Liwei Sichuan tangwei 11/21/92 98 88 85 85 356 89 2721030227 Wangtao Yunnan Huli 03/21/93 87 76 69 88 320 80
(8)在使用$0標識符輸出全部域時,也能夠省略後面的輸出動做。下面這條命令和上面的命令效果同樣:
[root@localhost test]# awk '$3 ~/(Sichuan|Yunnan)/' students 2821020225 Liulu Sichuan Lixia 01/23/93 89 76 88 72 325 81 2721010409 Liwei Sichuan tangwei 11/21/92 98 88 85 85 356 89 2721030227 Wangtao Yunnan Huli 03/21/93 87 76 69 88 320 80
(9)可使用算術運算符對域進行計算,這在製做一些統計信息時很是有用。例如要計算全部公共課的平均成績並輸出:
[root@localhost test]# awk '{print $1"\t"$2"\t"$6"\t"$7"\t"$8"\t"$6+$7+$8"\t"($6+$7+$8)/3}' students 2821020225 Liulu 89 76 88 253 84.3333 2821020115 Liumin 78 65 59 202 67.3333 2721020321 Xuli 76 81 85 242 80.6667 2921020632 Xiayu 78 86 92 256 85.3333 2721010409 Liwei 98 88 85 271 90.3333 2921050313 Heli 56 78 80 214 71.3333 2721030227 Wangtao 87 76 69 232 77.3333
(10)能夠結合使用關係運算符和邏輯運算符,以實現更復雜的運算。例如要輸出全部來自Sichuan而且平均分大於80的學生的信息:
[root@localhost test]# awk '($3=="Sichuan" && $11>80){print $0}' students 2821020225 Liulu Sichuan Lixia 01/23/93 89 76 88 72 325 81 2721010409 Liwei Sichuan tangwei 11/21/92 98 88 85 85 356 89
注意:使用awk時,必定要將多個模式和條件放在括號中,將執行編輯的語句放在單引號內,函數和流控制語句放入大括號內,避免產生錯誤。
awk中的變量有兩種:一種是內置變量,這些變量經常使用於控制輸出和保存「awk」當前工做狀態等信息,在引用變量時一般不須要使用美圓符號$;另外一種是用戶自定義變量,這些變量由用戶本身定義並使用,一般自定義變量放在BEGIN語句中初始化(賦值)。
使用自定義變量時,若是awk不能肯定自定義變量的類型,一般將其默認爲字符串進行處理。
FILENAME:用於保存輸入文件的文件名稱。
NF:用於保存當前正在處理的記錄的域個數。
NR:用於保存從文本中讀取記錄的個數。
FNR:用於保存當前讀取的記錄數。當輸入的文件有多個時,讀取新文件時,awk會重置這個變量。
OFS:用於設置輸出分隔字段的字符,默認爲空格。
FS:用於設置字段分隔符。
ORS:用於設置輸出記錄分隔符,默認爲新的一行。
RS:用於設置記錄分隔符,默認爲新行。
OFMT:數字的輸出格式。
ENVIRON:讀取環境變量。
(1)有時可能須要對輸出的文本從新格式化(設置你想要的輸出形式),這時可使用內置變量OFS和ORS,設置輸出文本的域分隔符和記錄分隔符。
[root@localhost test]# awk 'BEGIN{FS=":"; OFS="\t"; print "NF\tNR\tusername\tshell"}{print NF,NR,$1,$7}END{print FILENAME}' /etc/passwd NF NR username shell 7 1 root /bin/bash 7 2 bin /sbin/nologin 7 3 daemon /sbin/nologin 7 4 adm /sbin/nologin 7 5 lp /sbin/nologin 7 6 sync /bin/sync ......
(2)有時會遇到一些使用特殊分隔符的文本。例以下面這個示例中,須要在命令中把FS和RS從新設置成文本中使用的的特殊分隔符(而不是默認的空格和新行)才能順利讀取文本:
[root@localhost test]# cat students3 2821020225#Liulu#0#A#B#0\ 2821020115#Liumi#B#C#0#0\ 2721020321#Xuli#0#D#A#0\ 2921020632#Xiayu#A#C#0#0\ 2721010409#Liwei#B#C#0#D\ 2921050313#Heli#B#0#D#0\ 2721030227#Wangtao#C#0#D#0\
[root@localhost test]# awk 'BEGIN{FS="#"; RS="\\n\n"}{print $1,$2}' students3 2821020225 Liulu 2821020115 Liumi 2721020321 Xuli 2921020632 Xiayu 2721010409 Liwei 2921050313 Heli 2721030227 Wangtao
(3)當輸出的字符爲一個浮點數時,可能須要指定浮點數的輸出格式,此時可使用內置變量OFMT。例如:
[root@localhost test]# awk 'BEGIN{OFMT="%.2f"; print 19.243564}' 19.24
提示:使用awk命令時,若是沒有編輯語句,只有BEGIN語句,能夠沒必要輸入文件名直接運行。
(4)有時可能在處理過程當中須要引用環境變量,此時可使用ENVIRON變量。以下是一個使用ENVIRON讀取環境變量的示例:
[root@localhost test]# awk 'END{print ENVIRON["LANG"]}' students en_US.UTF-8
上面這個例子中雖然沒有引用文件中的內容,但使用了END表達式,所以必須使用文件參數,不然這條命令將變的不完整。
自定義變量一般用於統計並計算某個數字字段的結果,也可用於引用字符串。
(1)下面是一個統計當前文件夾中全部文件佔用空間的示例:
[root@localhost test]# ls -l | awk 'BEGIN{A=0}{A=A+$5}END{print "The size of all files of the current directory is:"A}' The size of all files of the current directory is:13232
在上面這個示例中,首先在BEGIN表達式中定義了一個 變量A並初始化爲0.而後與ls命令輸出的第5個字段相加,獲得全部文件佔用的空間,最後在END表達式中將結果輸出。
(2)變量也能夠在動做語句以後添加,例如:
[root@localhost test]# awk '$2==NAME1{print $0}' NAME1="Liwei" students 2721010409 Liwei Sichuan tangwei 11/21/92 98 88 85 85 356 89
提示:awk中引用變量時,一般不須要使用引用符號。但爲了便於理解和閱讀,應將變量名稱大寫並加上引用符號。
在awk命令中,還內置了一些流程控制語句(簡稱流控制語句)。使用這些流控制語句,能夠完成更多複雜的抽取任務。
awk命令中常見的流控制語句有:if、while、do-while和for語句,這些語句的基本語法結構與c語言中的語句相似。
(1)if語句的基本格式:
if (條件表達式) { 語句塊1 } else { 語句塊2 }
(2)while語句基本格式:
while (條件表達式) { 語句塊 }
(3)do-while語句基本格式:
do { 語句塊 } while (條件表達式)
(4)for語句基本格式:
for (初始表達式;條件表達式;步長) { 語句塊 }
(5)控制語句:
continue:用於在循環語句中控制循環走向。執行到此語句時,當即從當前執行位置跳轉到循環開始處開始下一次循環。此語句用於標識後面的語句已經沒有必要執行了,必須當即開始下一次循環執行過程。
break:用於跳出循環。當執行到此語句時,循環當即中止,而後執行循環後面的語句。
next:使awk讀取文本的下一行。該語句告訴awk,當前行已經沒有任何價值,須要當即讀取文本的下一行。
exit:若是這條語句沒有出如今END表達式中,則當即跳轉去執行END中的語句(即直接跳過文件最後一行,執行END表達式中的語句)。若是出如今END表達式中,則awk命令當即中止執行並退出。
提示:因爲循環語句會致使命令變得很長,且影響閱讀,所以一般建議將循環語句放入腳本文件中執行。
流控制語句用法示例:
(1)例如要輸出文件students中,全部總成績320以上的學生信息:
[root@localhost test]# awk 'BEGIN{OFS="\t"}{if($10>=320) print $1,$2,$10}' students 2821020225 Liulu 325 2721020321 Xuli 321 2921020632 Xiayu 334 2721010409 Liwei 356 2721030227 Wangtao 320
(2)輸出來自Sichuan的學生中,總成績大於等於320的學生信息:
[root@localhost test]# awk 'BEGIN{OFS="\t"}{if($3=="Sichuan" && $10>=320) print $1,$2,$10}' students 2821020225 Liulu 325 2721010409 Liwei 356
(3)計算ls命令傳送過來的信息中,全部普通文件的大小和文件夾的個數:
[root@localhost test]# cat total.awk BEGIN{ #Initialize A and count. A=0; count=0; } { #if the first character of field 1 is '-', then add A and the value of field 5. if($1 ~/^-/) A+=$5; #if the first character of field 1 is 'd', then count plus 1. if($1 ~/^d/) count++; } END{ print "total:"A; #count-2:because there are current directory and up level directory. print count-2," directories."; }
[root@localhost test]# ls -al /etc | awk -f total.awk total:1423743 84 directories.
(4)Linux系統管理員常常須要關注網絡延時大小,以避免對應用服務的正常運做形成影響。最好的解決辦法是每隔一段時間,使用ping命令測試與某個服務器的鏈接,並統計延時。
下面是一個使用ping命令發送4個ICMP數據包測試雙向連通性,並用awk從中計算最大延時、最小延時和平均延時的示例:
[root@localhost test]# cat ping.awk BEGIN{ #使用從新定義變量FS的方法設置域分隔符爲冒號、等號及空格 FS="[:= ]"; AVG=0; MAX=0; MIN=0; } { for(I=1;I<9;I++) { #若是當前爲第二條記錄,則爲變量MAX、MIN、IP_ADDR賦值 if(NR==2) { MAX=$11; MIN=$11; IP_ADDR=$4; } if(NR>1 && NR<6) { #計算延時總和 AVG+=$11; if($11>MAX) MAX=$11; if($11<MIN) MIN=$11; } #若是記錄數大於6,則跳出循環並執行END中的表達式 if(NR>=6) exit; #不然,讀取系一條記錄,從新開始循環 next; } } END{ AVG=AVG/4; print "IP address:",IP_ADDR; print "Avg:",AVG,"ms"; print "Max:",MAX,"ms"; print "Min:",MIN,"ms"; }
爲了方便抽取數據,先在BEGIN語句中定義了3中域分隔符:冒號、等號和空格。
因爲ping命令返回的第1行是由ping自動生成的簡要提示信息,所以awk將忽略第1行。
第2個到第5個記錄返回了TTL值和延時等信息,後面則是一些統計信息,所以awk處理過程當中只處理第2個到第5個記錄。
awk命令中的函數分爲兩種:內置函數和用戶自定義函數。內置函數可能是一些用來處理字符串和數學運算的函數,用戶自定義函數則是由用戶本身定義的功能性函數。
awk命令中函數的用法與c語言中函數的用法十分類似。常見的內置函數以下:
sub(regexp, replacement [, target]):當模式regexp第1次出現時,用replacement替換。
gsub(regexp, repalcement [, target]):將與模式regexp匹配的內容所有用replacement替換。
index(string, find):返回與find匹配的字符串第1次在string中出現的位置。
length([string]):返回字符串string的長度。
match(string, regexp [, array]):返回與regexp字符串第1次匹配的位置,若是不存在則返回0.
split(string, array [, fieldsep]):將string按照字段分隔符filedsep進行分割,分割後的字段存入數組array,返回字符串數組元素個數。
printf(format, expr1 [, expr2, …]):格式化輸出函數。
substr(string, start, [, length]):從string中返回從start開始,長度爲length的子字符串。
tolower(string):將string中的大寫字符轉換成小寫。
toupper(string):將string中的小寫字符轉換成大寫。
print:輸出函數。
rand():返回一個0到1之間的隨機數。
system():將傳遞來的字符串按shell命令執行。
atan2(x, y):反正切函數。
cos(x):餘弦函數。
sin(x):正弦函數。
log(x):x的天然對數值。
exp(x):E的x次冪。
int(x):將x轉換爲整數類型。
sqrt(x):x的平方根。
用法示例:(示例文件students2.2內容以下)
[root@localhost test]# cat students2.2 2821020225 Liulu 0 A B 0 2821020115 Liumin B C 0 0 2721020321 Xuli 0 0 B D 2921020632 Xiayu 0 0 0 0 2721010409 Liwei C 0 0 0 2921050313 Heli A 0 A B 2721030227 Wangtao B B C 0
(1)使用sub函數能夠查找第1次出現的模式並替換。例如用sub函數修改匹配模式的記錄:
[root@localhost test]# awk '$1 ~/28210202/{print sub(/Liulu/,"Lilu",$0)"\n"$0}' students2.2 1 2821020225 Lilu 0 A B 0
在上面的命令執行後,在第1行返回了修改的次數(對應sub函數),第2行輸出了修改後的記錄(對應$0)。
(2)利用函數gsub將全部匹配到的大寫字母A替換成數字95,而後返回一共修改的次數:
[root@localhost test]# awk 'BEGIN{N=0}{N+=gsub(/A/,"95",$0); print $0}END{print N}' students2.2 2821020225 Liulu 0 95 B 0 2821020115 Liumin B C 0 0 2721020321 Xuli 0 0 B D 2921020632 Xiayu 0 0 0 0 2721010409 Liwei C 0 0 0 2921050313 Heli 95 0 95 B 2721030227 Wangtao B B C 0 3
因爲函數gsub執行一個記錄的替換就會返回一次,所以此處用N+=gsub()。不然只會返回最後一條記錄替換的次數。
(3)利用index函數能夠返回第1次匹配字符串的位置:
[root@localhost test]# awk '{print index($0, "L"); if(NR==2) exit}' students2.2 12 12
(4)利用length函數查看字符串長度:
[root@localhost test]# awk '{print length($0); if(NR==2) exit}' students2.2 25 26
(5)使用match函數查看匹配模式首次出現的位置:
[root@localhost test]# awk 'BEGIN{print match("Hello! Welcome to Beijing!", "B")}' 19
(6)利用split函數將字符串按指定的分隔符拆開,而後放入指定的數組中並返回數組的元素個數:
[root@localhost test]# awk 'BEGIN{print split("FV7H8-42D17-08D0Q-8QAG9-YPUZA",ARR,"-");for(I in ARR)print ARR[I]}' 5 8QAG9 YPUZA FV7H8 42D17 08D0Q
另外一種方式:
[root@localhost test]# awk 'BEGIN{AN=split("FV7H8-42D17-08D0Q-8QAG9-YPUZA",ARR,"-");print AN;for(I=1;I<=AN;I++)print ARR[I]}' 5 FV7H8 42D17 08D0Q 8QAG9 YPUZA
注意:數組下標從1開始。
提示:在awk中使用數組時,能夠不用先定義,也沒必要指出元素的個數。
(7)利用tolower函數將字符串中的大寫字符轉換成小寫:
[root@localhost test]# awk '$1=="2821020225"{print tolower($0)}' students2.2 2821020225 liulu 0 a b 0
(8)利用toupper函數將字符串中的小寫字符轉換成大寫:
[root@localhost test]# awk '$1=="2821020225"{print toupper($0)}' students2.2 2821020225 LIULU 0 A B 0
(9)利用rand函數返回一個0到1之間的隨機數:
[root@localhost test]# awk 'BEGIN{print rand()}' 0.237788
(10)sin、cos函數能夠計算正弦、餘弦:
[root@localhost test]# awk 'BEGIN{PI=3.1415926;print sin(PI/4),cos(PI/4)}' 0.707107 0.707107
(11)使用log函數能夠返回天然對數:
[root@localhost test]# awk 'BEGIN{print log(100)}' 4.60517
(12)exp函數能夠返回e的x次冪:
[root@localhost test]# awk 'BEGIN{print exp(256)}' 1.51143e+111
(13)sqrt函數能夠計算平方根:
[root@localhost test]# awk 'BEGIN{print sqrt(256)}' 16
(14)格式化輸出函數printf(與c函數裏的printf同樣),可使用修飾符對輸出的內容進行格式控制,其基本語法以下:
print format, [argument]…
printf經常使用的修飾符:
%c:做爲一個ASCII字符輸出。
%s:做爲一個字符串輸出。
%o:做爲一個八進制數輸出。
%x:做爲一個十六進制輸出。
%d:做爲一個整數輸出。
%e:做爲一個科學型浮點數輸出。
%f:做爲一個浮點型數字輸出。
%g:由awk決定浮點數輸出的形式。
除了以上一些修飾符外,awk也容許像c語言那樣使用「-」、域寬和「.」進一步細化輸出格式。
使用printf函數將數字100分別轉換成字符、八進制、十六進制數:
[root@localhost test]# awk 'BEGIN{A=100;printf "%c\n%o\n%x\n",A,A,A}' d 144 64
(15)使用printf的修飾符能夠將生成的數據轉換成相應的格式:
[root@localhost test]# awk 'BEGIN{printf "%e\n%f\n%g\n",exp(30),exp(30),exp(30)}' 1.068647e+13 10686474581524.462891 1.06865e+13
(16)有時在計算一個數字時要保留必定的精度,這時可使用「.」指定精度,例如:
[root@localhost test]# awk 'BEGIN{printf "%4.5f\n",sqrt(47)}' 6.85565
(17)從/etc/passwd中抽取用戶名和用戶使用的shell,使用%s對齊輸出:
[root@localhost test]# awk 'BEGIN{FS=":";printf "%-15s %-15s\n","username","shell";printf "------------------------------\n"}{printf "%-15s %-15s\n",$1,$7}' /etc/passwd username shell ------------------------------ root /bin/bash bin /sbin/nologin daemon /sbin/nologin adm /sbin/nologin lp /sbin/nologin ...
(18)、爲了使輸出的段對齊,這裏使用修飾符,例如:
[root@localhost test]# awk '{printf "%-10s\t%-10s\t%d\t%d\t%d\t%d\t%d\n",$1,$2,$6,$7,$8,$6+$7+$8,($6+$7+$8)/3}' students 2821020225 Liulu 89 76 88 253 84 2821020115 Liumin 78 65 59 202 67 2721020321 Xuli 76 81 85 242 80 2921020632 Xiayu 78 86 92 256 85 2721010409 Liwei 98 88 85 271 90 2921050313 Heli 56 78 80 214 71 2721030227 Wangtao 87 76 69 232 77
在awk命令中,還容許使用用戶自定義函數。與C語言中的函數做用範圍不一樣,自定義函數不管出如今命令中的什麼位置,做用都是全局的,而函數中定義的變量只做用於函數內部。若是須要從函數中返回一個值,可使用return語句。
在awk命令中自定義函數的基本格式:
function name([參數列表]) { 語句塊; [return ...]; }
在調用函數時,只須要使用函數名並加上要使用的參數列表便可,無須使用特別的語句。
(1)下面是一個計算文件students中學生公共課總成績和平均成績的例子:
[root@localhost test]# cat ex.awk function Add(A,B,C) { return A+B+C; } function Avg(A,B,C) { return (A+B+C)/3; } { printf "%-10s\t%-10s\t%d\t%d\t%d\t%d\t%d\n",$1,$2,$6,$7,$8,Add($6,$7,$8),Avg($6,$7,$8); } END{ printf "%s\n",FILENAME; } [root@localhost test]# awk -f ex.awk students 2821020225 Liulu 89 76 88 253 84 2821020115 Liumin 78 65 59 202 67 2721020321 Xuli 76 81 85 242 80 2921020632 Xiayu 78 86 92 256 85 2721010409 Liwei 98 88 85 271 90 2921050313 Heli 56 78 80 214 71 2721030227 Wangtao 87 76 69 232 77 students
(2)對students中的學生成績進行篩選,只輸出學號,學生姓名和課程中的最高成績並保存爲strudents_max.
爲實現上述功能,此處可使用條件三目運算符和數組兩種方法。爲此所以兩個腳本文件stu.awk1和stu.awk2:
[root@localhost test]# cat stu.awk1 function Max(Arr) { Ma=Arr[1]; for(I in Arr) { if(Ma<Arr[I]) Ma=Arr[I]; } return Ma; } { for(J=1;J<5;J++) Ar[J]=$(J+2); printf "%-10s\t%-10s\t%d\n",$1,$2,Max(Ar); } [root@localhost test]# cat stu.awk2 function Max(A,B,C,D) { A>B?A:A=B; C>D?C:C=D; Ma=(A>C)?A:C; return Ma; } { printf "%-10s\t%-10s\t%d\n",$1,$2,Max($3,$4,$5,$6); }
使用awk的選項f調用stu.awk腳本,並將結果顯示並保存到students_max中:
[root@localhost test]# awk -f stu.awk1 students | tee students_max 2821020225 Liulu 89 2821020115 Liumin 78 2721020321 Xuli 85 2921020632 Xiayu 92 2721010409 Liwei 98 2921050313 Heli 80 2721030227 Wangtao 88 [root@localhost test]# awk -f stu.awk2 students | tee students_max2 2821020225 Liulu 89 2821020115 Liumin 78 2721020321 Xuli 85 2921020632 Xiayu 92 2721010409 Liwei 98 2921050313 Heli 80 2721030227 Wangtao 88
注意:在C語言中A>B?A:(A=B)能夠簡寫成A>B?:(A=B),但在awk中這是不容許的。
(3)最後,引用一個使用awk監視磁盤並向用戶發出警告的例子。在這個例子中,當某一個文件系統空閒不足10%時,經過system函數運行mail命令向管理員發出警告信息。
使用的腳本和執行過程以下:
[root@localhost test]# cat df.awk function mess(A,B,C) { if(B>=0.9) { system("date +'%F %r'>/root/df.tmp; df -h>>/root/df.tmp; cat /root/df.tmp | mail -s 'Disk Warning' 1025399680@qq.com,root;rm -rf /root/df.tmp"); printf "Disk Warning!\nFile System:%s\nUsed:%3.0f\nMounted on:",A,B*100,C; } } { if($1 ~/^\/dev\/sd/) { N=$3/$2; if(N>=0.9) mess($1,N,$6); } }
[root@localhost test]# df | awk -f df.awk
注意:在調用函數system時,必定要將shell命令放入雙引號中,shell命令本來應放入雙引號中的參數此時應該放入單引號中。