1. awk介紹
awk是一個強大的文本分析工具,相對於grep的查找,sed的編輯,awk在其對數據分析並生成報告時,顯得尤爲強大。簡單來說awk就是把文件逐行的讀入,以空格爲默認分隔符將每行切片,切開的部分再進行各種分析處理。
awk有3個不同版本: awk、nawk和gawk
,未作特別說明,一般指gawk,gawk 是 AWK 的 GNU 版本。
awk其名稱得自於它的創始人 Alfred Aho
、Peter Weinberger
和 Brian Kernighan
姓氏的首個字母。實際上 AWK 的確擁有自己的語言: AWK 程序設計語言 , 三位創建者已將它正式定義爲“樣式掃描和處理語言”。它允許您創建簡短的程序,這些程序讀取輸入文件、爲數據排序、處理數據、對輸入執行計算以及生成報表,還有無數其他的功能。
2. awk原理
AWK 工作流程可分爲三個部分:
- 讀輸入文件之前執行的代碼段(由BEGIN關鍵字標識)。
- 主循環執行輸入文件的代碼段。
- 讀輸入文件之後的代碼段(由END關鍵字標識)。
命令結構:
awk 'BEGIN{ commands } pattern{ commands } END{ commands }'
下面的流程圖描述出了 AWK 的工作流程:
- 1、通過關鍵字 BEGIN 執行 BEGIN 塊的內容,即 BEGIN 後花括號 {} 的內容。
- 2、完成 BEGIN 塊的執行,開始執行body塊。
- 3、讀入有 \n 換行符分割的記錄。
- 4、將記錄按指定的域分隔符劃分域,填充域,$0 則表示所有域(即一行內容),n 表示第 n 個域。
- 5、依次執行各 BODY 塊,pattern 部分匹配該行內容成功後,纔會執行 awk-commands 的內容。
- 6、循環讀取並執行各行直到文件結束,完成body塊執行。
- 7、開始 END 塊執行,END 塊可以輸出最終結果。
3.實例
3.1 awk [選項] ‘{編輯指令}’ 文件
awk '{print $1,$2}' /etc/rc.local //輸出文件的第1、2列
head -7 /etc/passwd > passwd.txt
awk -F ":" '{print $1","$7}' passwd.txt //以“:”作爲分隔符,,打印第1和7列
awk -F [:/] '{print $1,$10}' passwd.txt //以“:”或“/”作爲分隔符,,打印第1和10列
awk -F: '{print NR,NF}' passwd.txt // 打印行數與字段數
free | awk '/Mem/{printf("RAM Usage: %.2f\n"), $3/$2*100}'| awk '{print $3}'
3.2 與管道結合
ifconfig eth0 | grep "inet" | awk '{print $2}
df -hT / | tail -1 | awk '{print $6}'
uname -a | awk '{print $1,$3,$12}' //輸出第1、3、12字段
seq 100 | awk 'NR%7==0||NR~/7/{print}' #列出100以內整數中7的倍數或是含7的數
seq 100 | awk '$0%7==0||$0~/7/{print}'
行號與每行的實際文本值是一致的,那麼根據NR或者$0行值進行判斷都是可以的。輸出100以內7的倍數或是包含7的數
3.3 awk [選項] ‘[條件]{編輯指令}’ 文件
awk -F: '/bash$/{print}' passwd.txt #以bash結尾
awk -F: '/^[a-d]/{print $1,$6}' passwd.txt #以a-d任意字符開頭的第1和6列字段打印
awk -F: '/^a|nologin$/{print $1,$7}' passwd.txt #以a開頭或nologin結尾的第1和7列字段打印
awk -F: '$6~/bin$/{print $1,$6}' passwd.txt #以bin結尾的打印第1和6列
awk -F: '$7!~/nologin$/{print $1,$7}' passwd.txt #輸出其中登錄Shell不以nologin結尾(對第7個字段做!~反向匹配)的用戶名、登錄Shell信息
awk -F: 'NR==3{print}' passwd.txt #輸出第3行(行號NR等於3)的用戶記錄
awk -F: 'NR%2==1{print}' passwd.txt #輸出奇數行(行號NR除以2餘數爲1)的用戶記錄
awk -F: 'NR<=3{print}' passwd.txt #輸出前3行文本
awk -F: 'NR>=5{print}' passwd.txt #輸出從第5行開始到文件末尾的所有行
awk -F: '$1=="sync"{print}' passwd.txt #輸出用戶名爲“sync”的行
awk -F: '$1==ENVIRON["USER"]{print $1,$6,$7}' passwd.txt #輸出當前用戶的用戶名、宿主目錄、登錄Shell信息
awk -F: 'NR>=3&&NR<=5{print}' passwd.txt
awk -F: '(NR>=3)&&(NR<=5){print}' passwd.txt
awk -F: 'NR==3||NR==5{print}' passwd.txt
awk -F: '$7!~/nologin$/||$1~/^[ad]/{print}' passwd.txt #輸出“登錄Shell不以nologin結尾”或者“用戶名以a或d開頭”的文本
awk -F: '$3<3||$3%2==0{print}' passwd.txt #輸出UID小於3或者UID是偶數的用戶記錄
awk -F: '$3>=501&&$3<=505{print}' /etc/passwd #輸出UID大於501並且小於505的行
awk -F: '/^127|^192/{print}' /etc/hosts #輸出/etc/hosts映射文件內以127或者192開頭的記錄
3.3 awk [選項] ’ BEGIN{編輯指令 } {編輯指令} END{編輯指令}’ 文件
#打印A乘於2.56的值
awk 'BEGIN{A=1024;print A*2.56}'
#統計系統中使用bash作爲登錄Shell的用戶總個數
awk 'BEGIN{x=0}/\<bash$/{x++} END{print x}' /etc/passwd
#輸出的內容包括三個部分:列表頭、用戶信息、列表尾
awk -F: 'BEGIN{print "User\tUID\tHome"} {print $1"\t"$3"\t"$6} END{print "Total "NR" lines."}' /etc/passwd
#打印行數
awk -F"\t" 'BEGIN{x=0}{x+=NF}END{print x}' /etc/passwd
#以統計passwd.txt文件中以“:”分隔的總字段個數,需要每處理一行時將當前行的字段數(內置變量
awk -F: 'BEGIN{x=0}{x+=NF} END{print "Total "x" fields."}' passwd.txt
#統計/etc/passwd文件中UID小於或等於500的用戶個數
awk -F: 'BEGIN{i=0}{if($3<=500){i++}}END{print i}' /etc/passwd
#統計/etc/passwd文件中登錄Shell是“/bin/bash”的用戶個數
awk -F: 'BEGIN{i=0}{if($7~/bash$/){i++}}END{print i}' /etc/passwd
#統計/etc/passwd文件中登錄Shell不是“/bin/bash”的用戶個數
awk -F: 'BEGIN{i=0}{if($7!~/bash$/){i++}}END{print i}' /etc/passwd
#分別統計/etc/passwd文件中UID小於或等於500、UID大於500的用戶個數
awk -F: 'BEGIN{i=0;j=0}{if($3<=500){i++}else{j++}}END{print i,j}' /etc/passwd
#分別統計/etc/passwd文件中登錄Shell是“/bin/bash”、 登錄Shell不是“/bin/bash”的用戶個數
awk -F: 'BEGIN{i=0;j=0}{if($7~/bash$/){i++}else{j++}} END{print i,j}' /etc/passwd
分別統計/etc/passwd文件中登錄Shell是“/bin/bash”、“/sbin/nologin”、其他的用戶個數
awk -F: 'BEGIN{i=0;j=0;k=0}{if($7~/bash$/){i++} else if($7~/nologin$/){j++}else{k++}}END{print i,j,k}' /etc/passwd
#提取IP地址及訪問量
awk '{ip[$1]++} END{for(i in ip) {print i,ip[i]}}' /var/log/httpd/access_log
#用戶登陸次數
who | awk '{IP[$1]++}END{for(i in IP){print i,IP[i]}}'
列出黑名單)根據此可作防火牆
awk '{IP[$1]++}END{for(i in IP){print IP[i],i}}' /var/log/httpd/access_log | awk '$1>=500{print $2}' >>ip.log
#對第1)步的結果根據訪問量排名
awk '{ip[$1]++} END{for(i in ip) {print i,ip[i]}}' /var/log/httpd/access_log | sort -nr -k 2
求最大值:
awk 'BEGIN {max = 0} {if ($1+0 > max+0) max=$1} END {print "Max=", max}' test.txt
Max= 118
求最小值:
awk 'BEGIN {min = 65536} {if ($1+0 < min+0) min=$1} END {print "Min=", min}' test.txt
Min= 9
求和:
cat test.txt|awk '{sum+=$1} END {print "Sum= ", sum}'
Sum= 236
求平均值:
cat test.txt|awk '{sum+=$1} END {print "Avg= ", sum/NR}'
Avg= 39.3333
3.4 獲取外部變量方法
3.4.1 獲得普通外部變量
語法:
awk ‘{action}’ 變量名=變量值
這樣傳入變量,可以在action中獲得值。 注意:變量名與值放到’{action}’後面。
$ test='awk code'
$ echo | awk '{print test}' test="$test"
awk code
3.4.2 BEGIN程序塊中變量
格式如:
awk –v 變量名=變量值 [–v 變量2=值2 …] 'BEGIN{action}’
注意:用-v 傳入變量可以在3中類型的action 中都可以獲得到,但順序在 action前面。
[chengmo@localhost ~]$ test='awk code'
[chengmo@localhost ~]$ echo | awk -v test="$test" 'BEGIN{print test}'
awk code
[chengmo@localhost ~]$ echo | awk -v test="$test" '{print test}'
awk code
3.4.3 獲得環境變量
$ awk 'BEGIN{for (i in ENVIRON) {print i"="ENVIRON[i];}}'
AWKPATH=.:/usr/share/awk
SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass
SELINUX_LEVEL_REQUESTED=
SELINUX_ROLE_REQUESTED=
LANG=en_US.UTF-8
.......
只需要調用:
awk內置變量 ENVIRON
,就可以直接獲得環境變量。它是一個字典數組。環境變量名 就是它的鍵值。
4. 內建變量FS,NF,NR,RT,RS,ORS,OFS
4.1 FS 指定字段un列分隔符(Font Space)
$ echo "111|222|333" | awk '{print $1}'
111|222|333
$ echo "111|222|333" | awk 'BEGIN{FS="|"}{print $1}'
111
4.2 OFS 指定輸出字段列分隔符(Output Font space)
$ echo "111 222 333" |awk 'BEGIN{OFS="|";}{print $1,$2,$3}'
111|222|333
4.3 RS指定行分隔符 默認分隔符爲\n(Row Space)
$ echo "111 222|333 444|555 666" | awk 'BEGIN{RS="|"}{print $0}'
111 222
333 444
555 666
4.4 ORS指定輸出行分隔符
$ awk 'BEGIN{ORS="|";}{print $0;}' test.txt
111 222|333 444|555 666
4.5 RT 代指分隔符
$ echo "111 222|333 444|555 666" | awk 'BEGIN{RS="|"}{print $0,RT}'
111 222 |
333 444 |
555 666 |
4.6 NF 每行字段總數(Number of Font)
$ cat test.txt
111 222
333 444
555 666
$ awk '{print NF}' test.txt
2
2
2
$ awk '{print $NF}' test.txt
222
444
666
4.7 NR 當前行數(Number of Row)
$ cat test.txt
111 222
333 444
555 666 777
$ awk '{print NR}' test.txt
1
2
3
$ awk '{print $NR}' test.txt
111
444
777
5. awk腳本
關於awk腳本,我們需要注意兩個關鍵詞BEGIN和END。
BEGIN{ 這裏面放的是執行前的語句 }
END {這裏面放的是處理完所有的行後要執行的語句 }
{這裏面放的是處理每一行時要執行的語句}
假設有這麼一個文件(學生成績表):
$ cat score.txt
Marry 2143 78 84 77
Jack 2321 66 78 45
Tom 2122 48 77 71
Mike 2537 87 97 95
Bob 2415 40 57 62
$ cat cal.awk
#!/bin/awk -f
#運行前
BEGIN {
math = 0
english = 0
computer = 0
printf "NAME NO. MATH ENGLISH COMPUTER TOTAL\n"
printf "---------------------------------------------\n"
}
#運行中
{
math+=$3
english+=$4
computer+=$5
printf "%-6s %-6s %4d %8d %8d %8d\n", $1, $2, $3,$4,$5, $3+$4+$5
}
#運行後
END {
printf "---------------------------------------------\n"
printf " TOTAL:%10d %8d %8d \n", math, english, computer
printf "AVERAGE:%10.2f %8.2f %8.2f\n", math/NR, english/NR, computer/NR
}
我們來看一下執行結果:
$ awk -f cal.awk score.txt
NAME NO. MATH ENGLISH COMPUTER TOTAL
---------------------------------------------
Marry 2143 78 84 77 239
Jack 2321 66 78 45 189
Tom 2122 48 77 71 196
Mike 2537 87 97 95 279
Bob 2415 40 57 62 159
---------------------------------------------
TOTAL: 319 393 350
AVERAGE: 63.80 78.60 70.00
6. AWK 用戶自定義函數
用戶自定義函數的語法格式爲:
function function_name(argument1, argument2, ...)
{
function body
}
- function_name 是用戶自定義函數的名稱。函數名稱應該以字母開頭,其後可以是數字、字母或下劃線的自由組合。AWK
保留的關鍵字不能作爲用戶自定義函數的名稱。 - 自定義函數可以接受多個輸入參數,這些參數之間通過逗號分隔。參數並不是必須的。我們也可以定義沒有任何輸入參數的函數。
- function body 是函數體部分,它包含 AWK 程序代碼。
求最大值與最小值
# 返回最小值
function find_min(num1, num2)
{
if (num1 < num2)
return num1
return num2
}
# 返回最大值
function find_max(num1, num2)
{
if (num1 > num2)
return num1
return num2
}
# 主函數
function main(num1, num2)
{
# 查找最小值
result = find_min(10, 20)
print "Minimum =", result
# 查找最大值
result = find_max(10, 20)
print "Maximum =", result
}
# 腳本從這裏開始執行
BEGIN {
main(10, 20)
}
執行 functions.awk 文件,可以得到如下的結果:
$ awk -f functions.awk
Minimum = 10
Maximum = 20
7. AWK 數組
AWK 可以使用關聯數組這種數據結構,索引可以是數字或字符串。
AWK關聯數 組也不需要提前聲明其大小,因爲它在運行時可以自動的增大或減小。
數組使用的語法格式:
array_name[index]=value
- array_name:數組的名稱
- index:數組索引
- value:數組中元素所賦予的值
7.1 創建數組
接下來看一下如何創建數組以及如何訪問數組元素:
$ awk 'BEGIN {
sites["runoob"]="www.runoob.com";
sites["google"]="www.google.com"
print sites["runoob"] "\n" sites["google"]
}'
執行以上命令,輸出結果爲:
www.runoob.com
www.google.com
7.2 刪除數組元素
我們可以使用 delete 語句來刪除數組元素,語法格式如下:
delete array_name[index
下面的例子中,數組中的 google 元素被刪除(刪除命令沒有輸出):
$ awk 'BEGIN {
sites["runoob"]="www.runoob.com";
sites["google"]="www.google.com"
delete sites["google"];
print fruits["google"]
}'
輸出爲空。
7.3 多維數組
下面是模擬二維數組的例子:
$ awk 'BEGIN {
array["0,0"] = 100;
array["0,1"] = 200;
array["0,2"] = 300;
array["1,0"] = 400;
array["1,1"] = 500;
array["1,2"] = 600;
# 輸出數組元素
print "array[0,0] = " array["0,0"];
print "array[0,1] = " array["0,1"];
print "array[0,2] = " array["0,2"];
print "array[1,0] = " array["1,0"];
print "array[1,1] = " array["1,1"];
print "array[1,2] = " array["1,2"];
}'
執行上面的命令可以得到如下結果:
array[0,0] = 100
array[0,1] = 200
array[0,2] = 300
array[1,0] = 400
array[1,1] = 500
array[1,2] = 600