文本處理三劍客之AWK

個人博客地址:http://www.pojun.tech/ 歡迎訪問 

本文部分資料參考自 http://www.gnu.org/software/gawk/manual/gawk.html 。這是GAWK的官方幫助手冊,如果想深入學習的話,可以參考此類文檔。

其他參考資料:https://coolshell.cn/articles/9070.html

AWK簡介

AWK是一件上古神器,用這句話來形容AWK是最貼切不過了。  縱觀計算機發展的歷史,我們發現,awk幾乎是伴隨着計算機操作系統的發展一路走來。1970年作爲計算機計時元年,誕生了UNIX和C語言這兩種偉大的事物。在這之後,計算機技術飛速發展,1977年貝爾實驗室搞出來一款文本處理神器AWK,之所以起名AWK是因爲採用了三位創始人Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的名字首字母。想想也是比較有意思的一件事情。  1991年,linus開源了Linux操作系統,至此,計算機進入了迅猛發展的階段。AWK作爲一種神器自然而然地整合到了Linux中,於是作爲文本處理三劍客之一的AWK就這樣一直使用到了現在。  awk有3個不同版本: awk、nawk和gawk,未作特別說明,一般指gawk,gawk 是 awk 的 GNU 版本。AWK其實是一種類似編程語言的存在,它集成了編程語言的諸多特性,使用起來靈活多變。本文將簡要介紹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 常用選項

  • -F :指明輸入時用到的字段分隔符

  • -v var=value : 自定義變量

pattern和action

AWK program 的基本格式爲 program:pattern{action statements;..} 對於AWK來說,是支持正則表達式(pattern)的,由此可見正則表達式在計算機應用中是多麼的重要。

  • pattern部分決定動作語句何時觸發及觸發事件,如BEGIN,END

  • action statements對數據進行處理,放在{}內指明,如 print printf

分割符、域(也可以理解爲列,字段,屬性…)和記錄(可以理解爲一條數據,記錄)

這裏應該詳細的介紹一下AWK的處理機制。AWK 通常被用作報告生成器,格式化文本輸出。
就拿我們最熟悉的 /etc/passwd 文件來說,如果我們指定的分隔符爲冒號(:),那麼AWK會將這段文本中被冒號隔開的字符理解爲域,文本中的每一行作爲一條記錄。 就像下圖表現的這樣。

awk.png

那麼 具體來說,到底應該如何使用呢?

# 取出當前系統中所有的用戶 和密碼
# 實際上就是取出 /etc/paswdd   的第一列和第三列
[root@localhost ~]#awk -F: -v  OFS="**" '{print $1,$3}' /etc/passwd
root**0
bin**1
daemon**2
adm**3
lp**4
sync**5
shutdown**6
halt**7
.....

AWK 工作原理

第一步:執行BEGIN{action;… }語句塊中的語句 第二步:從文件或標準輸入(stdin)讀取一行,然後執行pattern{action;… }語句塊,它逐行掃描文件,從第一行到最後一行重複這個過程,直到文件全部被讀取完畢。 第三步:當讀至輸入流末尾時,執行END{action;…}語句塊。
BEGIN語句塊在awk開始從輸入流中讀取行之前被執行,這是一個可選的語句塊,比如變量初始化、打印輸出表格的表頭等語句通常可以寫在BEGIN語句塊中。
END語句塊在awk從輸入流中讀取完所有的行之後即被執行,比如打印所有行的分析結果這類信息彙總都是在END語句塊中完成,它也是一個可選語句塊。
pattern語句塊中的通用命令是最重要的部分,也是可選的。如果沒有提供pattern語句塊,則默認執行{ print },即打印每一個讀取到的行,awk讀取的每一行都會執行該語句塊。

AWK 變量

AWK 中內置了很多的變量,同時也可以自定變量來使用。下面介紹一下AWK中內置的變量。

變量名稱作用舉例
FS輸入字段分隔符,默認爲空白字符

awk -v FS=':' '{print $1,FS,$3}' /etc/passw

dawk –F: '{print $1,$3,$7}' /etc/passwd

OFS輸出字段分隔符,默認爲空白字符awk -v FS=':' -v OFS=':' '{print $1,$3,$7}' /etc/passwd
RS輸入記錄分隔符,指定輸入時的換行符,原換行符仍有效awk -v RS=' ' '{print }' /etc/passwd
ORS輸出記錄分隔符,輸出時用指定符號代替換行符awk -v RS=' ' -v ORS='###' '{print }' /etc/passwd
NF字段數量

awk -F: '{print NF}' /etc/fstab

awk -F: '{print $(NF-1)}' /etc/passwd

NR行號awk '{print NR}' /etc/fstab ; awk END'{print NR}' /etc/fstab
FNR各文件分別計數,行號awk '{print FNR}' /etc/fstab /etc/inittab
FILENAME當前文件名awk '{print FILENAME}' /etc/fstab
ARGC命令行參數的個數

awk '{print ARGC}' /etc/fstab /etc/initta

bawk 'BEGIN {print ARGC}' /etc/fstab /etc/inittab

ARGV數組,保存的是命令行所給定的各參數

awk 'BEGIN {print ARGV[0]}' /etc/fstab /etc/initta

bawk 'BEGIN {print ARGV[1]}' /etc/fstab /etc/inittab

printf 命令

AWK中有一個與print類似的命令叫做printf。printf能夠對處理之後的字符串格式化輸出。 在shell 中也有一個這樣的內置命令,二者的使用是類似的。同時在使用printf的時候也可以參考C語言的輸出方式來理解。

格式化輸出:printf “FORMAT”, item1, item2, …
(1) 必須指定FORMAT
(2) 不會自動換行,需要顯式給出換行控制符,\n
(3) FORMAT中需要分別爲後面每個item指定格式符

格式符:與item一一對應

  • %c: 顯示字符的ASCII碼

  • %d, %i: 顯示十進制整數

  • %e, %E:顯示科學計數法數值

  • %f:顯示爲浮點數

  • %g, %G:以科學計數法或浮點形式顯示數值

  • %s:顯示字符串

  • %u:無符號整數

  • %%: 顯示%自身

修飾符:

  • #[.#]:第一個數字控制顯示的寬度;第二個#表示小數點後精度,%3.1f

  • -: 左對齊(默認右對齊) %-15s

  • +:顯示數值的正負符號 %+

試驗一 取出當前系統中所有的用戶 和密碼

# 取出當前系統中所有的用戶 和密碼
# 實際上就是取出 /etc/paswdd   的第一列和第三列
[root@localhost ~]#awk -F: '{printf "%-20s %-10d \n" ,$1,$3}' /etc/passwd
root                 0          
bin                  1          
daemon               2          
adm                  3          
lp                   4          
sync                 5          
shutdown             6    

.......

試驗二 取出當前系統中硬盤的使用率

# 取出當前系統中硬盤的使用率
[root@localhost ~]#df | grep '^/dev/sd'| awk '{printf "Devname:%s Use:%s\n",$1,$5}'
Devname:/dev/sda2 Use:22%
Devname:/dev/sda3 Use:1%
Devname:/dev/sda1 Use:17%

AWK 模式匹配

AWK 支持模式匹配,也就是說,AWK根據pattern條件,過濾匹配的行,再做處理

  • 如果未指定:空模式,匹配每一行

  • /regular expression/:僅處理能夠模式匹配到的行,需要用/ /括起來 ,這與sed是相似的。

  • relational expression: 關係表達式,結果爲“真”纔會被處理

    • 真:結果爲非0值,非空字符串

    • 假:結果爲空字符串或0值

  • line ranges:行範圍 startline,endline:/pat1/,/pat2/ 不支持直接給出數字格式 ,處理匹配這兩個模式之間的行的範圍。

  • BEGIN/END模式:

    • BEGIN{}: 僅在開始處理文件中的文本之前執行一次

    • END{}:僅在文本處理完成之後執行一次

#打印 root 行到nobody行之間的第一列
[root@localhost ~]#awk -F: '/^root\>/,/^nobody\>/{print $1}' /etc/passwd
root
bin
daemon
adm
lp
sync
shutdown
halt
mail
operator
games
ftp
nobody

AWK 控制語句

前面我們說過,AWK是一種類似於編程語言的文本處理器。之所以這樣說,是因爲AWK中支持豐富的控制語句。這些控制語句與C語言很相似,可以結合C語言來理解,在使用上比shell要簡單很多。

AWK 中的控制結構舉例

  • { statements;… } 組合語句

  • if(condition) {statements;…} if語句

  • if(condition) {statements;…} else {statements;…} if else 語句

  • while(conditon) {statments;…} while 語句

  • do {statements;…} while(condition) do while 循環

  • for(expr1;expr2;expr3) {statements;…} for 循環

  • switch case 語句

  • break 語句

  • continue 語句

  • delete array[index]

  • delete array

  • exit

試驗一 /etc/passwd 中UID 小於1000輸出系統用戶,大於1000 輸出普通用戶

[root@localhost ~]#awk -F: '{if($3>=1000){printf "Common User: %s \n",$1}else{printf "root or sysuser:%s\n",$1}}' /etc/passwd  
root or sysuser:root
root or sysuser:bin
root or sysuser:daemon
root or sysuser:adm
......
Common User: basher 
Common User: sh 
Common User: nologin 
Common User: quliang 
root or sysuser:apache
root or sysuser:quagga

試驗二 輸出/etc/grub2.cfg 文件中以linux16開頭的行中每個字段的長度

[root@localhost ~]#awk '/^[[:space:]]*linux16/{i=1;while(i<NF){print $i,length($i);i++}}' /etc/grub2.cfg
linux16 7
/vmlinuz-3.10.0-514.el7.x86_64 30root=UUID=0115740e-4b33-4387-bd21-722a969f7fd8 46
ro 2crashkernel=auto 16
rhgb 4
quiet 5
linux16 7
/vmlinuz-0-rescue-ecc418eb09cb4d05969fbaafae323000 50root=UUID=0115740e-4b33-4387-bd21-722a969f7fd8 46
ro 2crashkernel=auto 16
rhgb 4
quiet 5

AWK 函數

AWK 函數的使用方式與它的控制結構一樣,與C語言很類似,可以結合下面的示例好好的理解一下。 
同時AWK本身有一些簡單易用的庫函數,可以直接在AWK命令中進行使用。

內置函數

AWK 中的內置函數如下表所示。

變量名稱作用
rand()返回0和1之間一個隨機數
length([s])返回指定字符串的長度
sub(r,s,[t])對t字符串進行搜索r表示的模式匹配的內容,並將第一個匹配的內容替換爲s
gsub(r,s,[t])對t字符串進行搜索r表示的模式匹配的內容,並全部替換爲s所表示的內容
split(s,array,[r])以r爲分隔符,切割字符串s,並將切割後的結果保存至array所表示的數組中,第一個索引值爲1,第二個索引值爲2,…

試驗一 將8008:08:08 08:08:08 中冒號替換

[root@localhost ~]#echo "2008:08:08 08:08:08" | awk 'sub(/:/,"-",$1)'
2008-08:08 08:08:08

[root@localhost ~]#echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",$0)'
2008-08-08 08-08-08

試驗二 統計網絡訪問中同一IP 的訪問次數

[root@localhost ~]#netstat -tan | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}
END{for (i in count) {print i,count[i]}}'

0.0.0.0 6
172.18.251.85 1

自定義函數

自定義函數的格式 與C語言的函數格式很相似,並且還具有形式參數和實際參數,返回值也與bash 中有所不同。

function name ( parameter, parameter, ... ) {
	statements	
	return expression
}

我們定義一個函數,用來返回兩個值中的最大值

[root@localhost test]#cat f1.txtfunction max ( param1,param2) {
    param1 > param2 ? var=param1 :var=param2    
    return var
}

BEGIN{print max(a,b)}
[root@localhost test]#awk -v a=10 -v b=20 -f f1.txt 
20

AWK是十分強大的,通過上面的介紹我們也僅僅是介紹了AWK的一些皮毛而已,在實際生產中還需要根據實際需要查閱相關資料來實現我們的需求纔可以。




個人博客地址:http://www.pojun.tech/ 歡迎訪問

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章