一、awk介紹
Linux文本處理工具三劍客:grep、sed和awk。其中grep是一種文本過濾工具,sed是文本行編輯器,而awk是一種報表生成器,就是對文件進行格式化處理的,這裏的格式化不是文件系統的格式化,而是對文件內容進行各種“排版”,進而格式化顯示。
在Linux之上我們使用的是GNU awk 簡稱gawk,並且gawk是awk的鏈接文件。gawk是一種過程式編程語言。gawk還支持條件判斷、數組、循環等各種編程語言中所有可以使用的功能,因此還可以把gawk稱爲一種腳本語言解釋器。
二、awk用法
一個awk通常有BEGIN語句塊、能夠使用模式匹配的通用語句塊、END語句塊三部分組成,這三個部分是可選的,任意一個部分都可以不出現在腳本中。
2.1 基本用法:
awk [options] ‘program’ var=value file…
awk [options] -f programfile var=value file…
awk [options] 'BEGIN{ action;… } pattern{ action;… } END{ action;… }' file
其中,program:PATTERN{ACTION STATEMENTS}
program:編程語言
通常是被單引號或雙引號中
BEGIN/END:特殊模式
BEGIN:
在文件格式化操作開始之前事先執行的一次操作;通常用於輸出表頭或做出一個預處理操作
END:
在文件格式操作完成後,命令退出之前執行的一次操作;通常用於輸出表尾或做出清理操作
pattern:模式
決定動作語句何時觸發及觸發事件
ACTION STATEMENTS:動作語句
對數據進行處理,放在“{}”內指明,可以是有多個語句組成,各語句間使用分號間隔;如print,printf。
OPTIONS:
支持的選項 | 說明 |
---|---|
-f /PATH/FROM/AWK_SCRIPT --file program-file | 從文件接收 awk指令,可以同時指定多個文件 |
-F [] --field-separator fs | 指定(fs)列分隔符,指明輸入時用到的字段分隔符 |
-v var=value --assign var=value | 爲 BEGIN 塊定義變量var,指定其值爲value,自定義變量,變量賦值 |
2.2 分隔符、域和記錄
awk執行時,由分隔符分隔的字段(域)標記$1,$2...$n成爲域標識;$0爲所有域
文件的每一行稱爲記錄,也可以用別的作爲記錄的分隔符
省略action,默認執行print $0
2.3 工作原理
第一步:執行BEGIN{commands}語句塊中的語句;
第二步:從文件或標準輸入(stain)讀取一行,然後執行pattern{commands}語句塊,它逐行掃描,從第一行到最後一行重複這個過程,直到文件被完全讀取。
第三步:當讀到輸入流末尾時,執行END{commands}語句塊。
解釋說明:
BEGIN語句塊在awk開始從輸入流中讀取之前被執行,這是一個可選的語句塊,比如變量初始化、打印輸出表格的表頭等語句通常可以寫在BEGIN語句塊中。
pattern語句塊中的通用命令時最重要的部分,它也是可選的,如果沒有提供pattern語句塊,則是默認執行{print}。既打印每一個讀取到的行,awk讀取的每一行都會被執行該語句塊。
END語句塊在awk從輸入流中讀取完所有的行之後就被執行,比如打印所有行的 分析結果這類信息彙總都是在END語句塊中完成,他也是一個可選的語句塊。
三、介紹
3.1 awk的輸出命令之一:print
用法:
print item1,iitem2,...
item:字符串,用引號引用;
變量:顯示變量的值,可以直接使用變量的名進行引用;
數值:無須加引號
要點:
(1)各item之間需要使用逗號分隔,而輸出時的分隔符默認爲空白字符;
(2)輸出的各item可以爲字符串或數值、當前記錄的字段($#)、變量或awk的表達式,數值會被隱式轉換成字符串進行輸出;
(3)print後面的item省略時,相當於運行“print $0”,用於輸出整行;
(4)輸出空白字符:print“”
示例:
tail -3 /etc/fstab |awk '{print $2,$4}'
echo 1 2 3 |awk '{print $1,$2}'
3.2 變量
變量:內置和自定義變量
3.2.1 內置變量
允許使用美元符號($)和數據字段在數據行中位置對應的數值來引用該數據行中的字段
FS:輸入字段分隔符,默認爲空白字符
-v FS="[ , : . ]" 相當於 -F:
示例:
awk -v FS=':' '{print $1,FS,$3}’ /etc/passwd
awk –F: '{print $1,$3,$7}’ /etc/passwd
OFS:輸出字段分隔符,默認爲空白字符
awk -v FS=‘:’ -v OFS=‘:’ '{print $1,$3,$7}’ /etc/passwd
RS:輸入記錄分隔符,指定輸入時的換行符,原換行符仍有效
awk -v RS=':' '{print $1,$3,$7}' /etc/passwd
ORS:輸出記錄分隔符,輸出時用指定符號代替換行符
awk -v RS=':' -v ORS='###'‘{print }’ /etc/passwd
NF:當前行的字段數量
print NF:顯示當前行的字段數
print $NF:顯示當前行的第NF字段的值
示例:
顯示/etc/passwd每行的字段的數量
awk -F:'{print NF}' /etc/passwd
注:引用內置變量不用$
awk -F: '{print $(NF-1)}' /etc/passwd
NR:行號;命令後跟的所有文件將統一合併計數
示例:
顯示/etc/fstab文件的總行號和最後一行的行號
awk '{print NR}' /etc/fstab ; awk END'{print NR}' /etc/fstab
注:printf默認不換行,需要藉助END,而且順便藉助不換行完成單詞的拼接,模式數量必須與變量數量(列)對應起來。
FNR:各文件分別計數;行號
示例:
分別計算/etc/fstab和/etc/inittab兩個文件的行號
awk '{print FNR}' /etc/fstab /etc/inittab
FILENAME:當前正在被awk讀取的文件的文件名
示例:
顯示當前正在被awk讀取的/etc/fstab文件名
awk '{print FILENAME}’ /etc/fstab
ARGC:awk命令行的參數的個數
示例:
awk '{print ARGC}’ /etc/fstab /etc/inittab
awk ‘BEGIN {print ARGC}’ /etc/fstab /etc/inittab
ARGV:數組,保存的是命令行所給定的各參數
ARGC[index]
ARGC[0],ARGC[1]
示例:
awk ‘BEGIN {print ARGV[0]}’ /etc/fstab /etc/inittab
awk ‘BEGIN {print ARGV[1]}’ /etc/fstab /etc/inittab
awk ‘BEGIN {print ARGV[2]}’ /etc/fstab /etc/inittab
注:命令awk也是一個參數。
3.2.2 自定義變量(區分字符大小寫)
(1) -v var=value 變量名區分字符大小寫;
(2) 在program中直接定義
示例:
以/etc/passwd文件中的用戶爲例,顯示這些用戶的性別爲male,年齡爲18。
awk -F: '{sex="male";print $1,sex,age;age=18}' /etc/passwd
3.3 awk的輸出命令之二:printf格式化輸出
語法:
printf FORMAT,item1,item2,...
要點:
(1)必須提供FORMAT;
(2)與print語句不同,printf不會自動換行,需要顯示指定換行符:\n
(3)FORMAT中需要分別爲後面的每個item指定一個格式符,否則item無法顯示。
格式符:都以%開頭,後跟單個字符;
格式 鉚定符 | 效果說明 |
---|---|
%c | 顯示字符的ASCII碼 |
%s | 顯示爲字符串 |
%d, %i | 顯示爲十進制整數 |
%o | 無符號八進制數的整數部分 |
%e, %E | 科學計數法顯示數值 |
%u | 顯示無符號整數 |
%f | 顯示爲浮點數 |
%% | 顯示%符號自身 |
%10s | 預留10個字符位置,右對齊 |
%-10s | 左對齊顯示 |
%g,%G | 以科學計數法或浮點形式顯示數值 |
示例:
在文件/etc/issue中每行前添加“user:”並輸出
awk -F: '{printf "user:%s\n",$1}' /etc/issue
修飾符:每一種格式都有一些修飾符
#[.#]:
左邊的#:用於指定顯示寬度;
右邊的#:顯示精度
-:左對齊(默認右對齊)
+:顯示數值的正負符號
示例:
顯示文件/etc/passwd中用戶名和UID以“Username:%15s,UID:%d\n”形式輸出,用戶名向右對齊15個字符,UID以十進制整數輸出。
awk -F: '{printf "Username:%15s,UID:%d\n",$1,$3}' /etc/passwd
3.4 awk的操作符
awk的操作符有:算術操作符、字符操作符、賦值操作符、比較操作符、模式匹配操作符、邏輯操作符、條件表達式和函數調用。
算數操作符:實現一些算術運算:
x+y:加;
x-y:減;
x*y:乘;
x/y:除;
x^y:x的y次方;
x%y:取餘;x除以y得出的餘數
-x:轉換爲負值
+x:轉換爲數值
字符串操作符:沒有符號的操作符,字符串連接
賦值操作符:通常爲變量的賦值:
=:等於;
+=:自加;例,x+=y,意思是把x本身的值加上y的值再賦給x
-=: 自減;例,x-=y,意思是把x減去y的值再賦值給x
*=: 自乘;例,x*=y,意思是把x乘以y的值再賦值給x
/=: 自除;例,x/=y,意思是把x除以y的值再賦值給x
%=:取餘賦值;例,x%=y,意思是x除以y後的餘數賦值給x
^=: 次方;例,x^=y,意思是把x的y次方的值再賦值給x
++:遞加;例,i++,意思是把i的值加1再賦值給i,等同於i+=1
- -: 遞減;例,i- -,意思是把i的值減1再賦值給i,等同於i-=1
比較操作符:字符串或者數值的大小比較:
= =: 等於
!=: 不等於
>: 大於
>=: 大於等於
<: 小於
<= : 小於等於
模式匹配操作符:根據右側的模式進行匹配操作:
~:左邊是否匹配包含右邊
!~: 是否左邊和右邊是否不匹配包含
邏輯操作符:進行邏輯的運算:
&&:與運算
||:或運算
!:非
條件表達式(三目表達式):
格式:
selector?if-true-expression:if-false-expression
三目表達式顧名思義就是有三個表達式組成的,問號之前的表達式通常爲判斷表達式,問號之後的表達式是滿足條件纔會執行的語句,冒號之後的是當不滿足條件時纔會執行的語句。
示例:
awk -F: '{$3>=1000?usertype="common user":usertype="sysadmin or sysuser";printf "%15s: %-s\n" ,$1,usertype}' /etc/passwd
3.5 awk PATTERN
PATTERN:根據pattern條件,過濾匹配的行,再做處理
(1)如果未指定,empty:空模式,匹配每一行
(2) /regular expression/:僅處理能夠模式匹配到的行,需要用/ /括起來
示例:
顯示/etc/fstab文件中是UUID的行的第一段
awk '/^UUID/{print $1}' /etc/fstab
顯示/etc/fstab文件中沒有UUID的行的第一段
awk '!/^UUID/{print $1}' /etc/fstab
(3) relational expression: 關係表達式,結果爲“真”纔會被處理
真:結果爲非0值,非空字符串
假:結果爲空字符串或0值
(4)line ranges:行範圍
startline,endline:/pat1/,/pat2/ 不支持直接給出數字格式
示例:
awk -F: ‘/^root\>/,/^nobody\>/{print $1}' /etc/passwd
顯示/etc/passwd文件中10~20的用戶名
awk -F: ‘(NR>=10&&NR<=20){print NR,$1}' /etc/passwd
(5)BEGIN/END模式
BEGIN{}: 在文件格式化操作開始之前事先執行的一次操作;通常用於輸出表頭或做出一個預處理操作;
END{}:在文件格式化操作完成之後,命令退出之前執行的一次操作;通常用於輸出表尾或做出清理操作;
示例:
awk -F: 'BEGIN{print " USER UID \n--------------- "}{print $1,$3}'END{print "=============="} /etc/passwd
3.6 awk action
常用的action分類
(1) Expressions:算術,比較表達式等 (2) Control statements:if, while等 (3) Compound statements:組合語句 (4) input statements (5) output statements:print等
3.7 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
3.7.1 awk控制語句if-else
語法:
if(condition) { statement;…} [else statement] if(condition1){ statement1 } else if(condition2) { statement2 }else{ statement3 }
使用場景:對awk取得的整行或某個字段做條件判斷
示例:
顯示磁盤使用率大於20的磁盤名和使用率
df -h|awk -F% '/^\/dev/{print $1}'|awk '$NF>=20{print $1,$5}'
awk 'BEGIN{ test=100;if(test>90){print "very good"}else if(test>60){ print "good"}else{print "no pass"}}' awk 'BEGIN{ test=85;if(test>90){print "very good"}else if(test>60){ print "good"}else{print "no pass"}}' awk 'BEGIN{ test=59;if(test>90){print "very good"}else if(test>60){ print "good"}else{print "no pass"}}'
注:awk的if語句也支持else子句,允許在if語句條件不成立的情況下執行一條或多條語句,也可以在單行上使用else子句,但必須在if語句部分使用分號“:”.
3.7.2 while循環
語法:
while(condition){statement;…}
條件“真”,進入循環;條件“假”,退出循環
使用場景:
對一行內的多個字段逐一類似處理時使用
對數組中的各元素逐一處理時使用
示例:
顯示/etc/grub2.cfg文件中linux16/後跟代碼的字段的長度
awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {print $i,length($i); i++}}' /etc/grub2.cfg
awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {if(length($i)>=10) {print $i,length($i)}; i++}}' /etc/grub2.cfg
3.7.3 do-while循環
do-while語句類似於while語句,但會在檢查條件語句之前執行命令。格式爲:
do { statement;… }while(condition)
意義:無論真假,至少執行一次循環體,這種格式保證了語句會在條件被評估之前至少執行一次。
示例:
計算1~100相加的值
awk 'BEGIN{ total=0;i=0;do{ total+=i;i++;}while(i<=100);print total}'
3.7.4 for循環
語法:
for(expr1;expr2;expr3) {statement;…} for(variable assignment;condition;iteration process) {for-body}
特殊用法:
能夠遍歷數組中的元素
語法:
for(var in array) {for-body}
示例:
awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg
3.7.5 switch語句
語法:
switch(expression) {case VALUE1 or /REGEXP/: statement1; case VALUE2 or /REGEXP2/: statement2; ...; default: statementn}
3.7.6 break和continue
break [n]:退出當前循環,n是一個數字,用於指定退出幾層循環;
continue:提前結束本輪循環而進入下一輪;
示例:
計算1~100是偶數相加的值
awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i%2==0)continue;sum+=i}print sum}'
awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i==66)break;sum+=i}print sum}'
3.7.7 next
提前結束對本行文本處理,而提前進入下一行的處理操作(awk自身循環)
示例:
篩選/etc/passwd中UID能被2整除的數,並輸出用戶名和UID。
awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd
四、數組
awk編程語言使用關聯數組來提供數組功能,關聯數組跟數字數組不同之處在於它的索引值可以是任意文本字符串。每個索引字符串都必須是唯一的,並唯一地標識賦給它的數據元素。
關聯數組:
array[index-expression] index-expression:
(1) 可使用任意字符串;字符串要使用雙引號括起來
(2) 如果某數組元素事先不存在,在引用時,awk會自動創建此元素,並將其值初始化爲“空串”;若要判斷數組中是否存在某元素,要使用“index in array”格式進行遍歷
數組遍歷:
可以用for語句的一種特殊形式
格式:
for(var in array) { for-body }
注意:var會遍歷array的每個索引,這個for語句也會在每次將關聯數組array的下一個索引值賦給變量var時,執行一遍statements。重要的是記住這個變量是索引值而不是數據元素值。
示例:
統計netstat -tan中各狀態的次數
netstat -tan | awk '/^tcp/{state[$NF]++}END{for(i in state) { print i,state[i]}}'
注:每出現一被/^tcp/模式匹配到的行,數組S[$NF]就加1,NF爲當前匹配到的行的最後一個字段,此處用其值做爲數組S的元素索引。
統計訪問本終端的日誌中所有IP地址數量
awk '{ip[$1]++}END{for(i in ip) {print i,ip[i]}}' /var/log/httpd/access_log
五、awk函數
函數分爲內建函數和用戶自定義函數
5.1 內建函數
數值處理:
rand():返回0和1之間一個隨機數
示例:
輸出10個100以內的隨機數
awk 'BEGIN{srand(); for (i=1;i<=10;i++)print int(rand()*100) }'
字符串處理:
length([s]):返回指定字符串的長度
sub(r,s,[t]):對t字符串進行搜索r表示的模式匹配的內容,並將第一個匹配的內容替換爲s
示例:
把"2008:08:08 08:08:08"中第一個“:”替換成“-”
echo "2008:08:08 08:08:08" | awk 'sub(/:/,"-",$1)'
gsub(r,s,[t]):對t字符串進行搜索r表示的模式匹配的內容,並全部替換爲s所表示的內容
示例:
把"2008:08:08 08:08:08"中的第一段中的“:”全部替換成“-”
echo "2008:08:08 08:08:08" | awk ‘gsub(/:/,"-",$0)'
split(s,array,[r]):以r爲分隔符,切割字符串s,並將切割後的結果保存至array所表示的數組中,第一個索引值爲1,第二個索引值爲2,…
示例:
netstat -tan | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}END{for (i in count) {print i,count[i]}}'
注意:awk的數組下標從1開始編號,而非0.
substr(s,i[,n]):從 s 所表示的字符串中取子串,取法:從 i 表示的位置開始,取n個字符;
時間類函數:systime(),取當前時間,結果形式爲時間戳;
示例:
取/etc/passwd中的用戶名
awk '{split($0,userinfo,":");print userinfo[1]}' /etc/passwd
5.2 用戶自定義函數
要定義自己的函數,必須使用function關鍵字:
格式:
function name ( parameter, parameter, ... ) { statements return expression }
示例:
使用awk函數,2和3比大小
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
注:在定義函數時,它必須出現在所有代碼塊之前(包括BEGIN代碼塊),而且,不能將-f命令行參數和內聯gawk腳本放到一起使用,不過可以在同一個命令行中使用多個-f參數。