awk

簡介
awk是一個強大的文本分析工具,相對於grep的查找,sed的編輯,awk在其對數據分析並生成報告時,顯得尤其強大。簡單來講awk就是把文件逐行的讀入,以空格爲默認分隔符將每行切片,切開的部分再進行各類分析處理。
awk有3個不一樣版本: awk、nawk和gawk,未做特別說明,通常指gawk,gawk 是 AWK 的 GNU 版本。
awk其名稱得自於它的創始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首個字母。實際上 AWK 的確擁有本身的語言: AWK 程序設計語言 , 三位建立者已將它正式定義爲「樣式掃描和處理語言」。它容許您建立簡短的程序,這些程序讀取輸入文件、爲數據排序、處理數據、對輸入執行計算以及生成報表,還有無數其餘的功能。
 
流程
awk工做流程是這樣的:先執行BEGING,而後讀取文件,讀入有/n換行符分割的一條記錄,而後將記錄按指定的域分隔符劃分域,填充域,$0則表示全部域,$1表示第一個域,$n表示第n個域,隨後開始執行模式所對應的動做action。接着開始讀入第二條記錄······直到全部的記錄都讀完,最後執行END操做。
 
如圖:
 
awk內置變量
awk有許多內置變量用來設置環境信息,這些變量能夠被改變,下面給出了最經常使用的一些變量。
ARGC               命令行參數個數
ARGV               命令行參數排列
ENVIRON            支持隊列中系統環境變量的使用
FILENAME           awk瀏覽的文件名
FNR                瀏覽文件的記錄數
FS                 設置輸入域分隔符,等價於命令行 -F選項
NF                 瀏覽記錄的域的個數
NR                 已讀的記錄數
OFS                輸出域分隔符 
ORS                輸出記錄分隔符
RS                 控制記錄分隔符
ARGVIND            命令行參數索引(合併文件用過、)

  

FILENAME awk瀏覽的文件名NF 瀏覽記錄的域的個數 NR 已讀的記錄數OFS 命令行參數索引(合併文件用過、)
Q1:打印目錄下文件大小
A1:
[root@salt-node01 etc]# ls -al | awk -F " " 'BEGIN {filesize=0;print "[start]file size is",filesize;} {filesize=filesize+$5;} END{print "[end]file size is",filesize/1024,"K"}'
[start]file size is 0
[end]file size is 846.303 K
小結:
awk語法:-F配合「」指定分隔符,‘’用於指定pattern和action;BEGIN{}語句用於初始化變量和輸出提示信息,第二個大括號{}中的內容,按照指定的分隔符,生成不一樣的域值,將field的內容進行對應的匹配和操做,最後END{}輸出要表示的信息;若是在{}中須要進行for語句或者if語句,須要將循環條件或者判斷條件用()進行包裹,後邊緊跟{}進行statement語句。
此例中定義了一個變量filesize進行累計$5的操做,難度爲1
 
Q2:打印奇數行
[root@salt-node01 awk_test]# seq 10 | awk 'i=!i'
等價於[root@salt-node01 ~]# seq 10 | awk 'BEGIN{}i=!i{print $0}END{}'
1
3
5
7
9
分析:seq 10進行數據模擬,輸出1-10;awk處理流程,pattern和command的模式,此處無command,默認輸出爲$0,等價於seq 10 | awk '{i=!i;print $0}';此處只有pattern,pattern爲i=!i; awk處理變量,對於未定義的變量,認爲i是0,此處進行賦值i=1;1爲真則匹配改行,打印$0;而後i=0,,,,,,因此只打印奇數行
驗證:
[root@salt-node01 awk_test]# seq 10 | awk '{i=!i;print i}'
1 #打印
0 #不打印
1 #重複上述動做
0
1
0
1
0
1
0
Q3:如何打印偶數行?(BEGIN對i進行賦值便可)
[root@salt-node01 awk_test]# seq 10 | awk 'BEGIN{i=1} i=!i;'
2
4
6
8
10
Q4:行去重
進階題:
[root@salt-node01 awk_test]# cat uniq.text
bbb
bbb
bbb
ccc
ccc
ccc
aaa
aaa
aaa
aaa
aaa
aaa
aaa 
請使用awk命令進行數據去重。
[root@salt-node01 awk_test]# awk '!a[$0]++' uniq.text
bbb
ccc
aaa
分析:awk處理流程略,處理流程見上例;此處pattern是'!a[$0]++,C語言中++是先引用後賦值,此處a[bbb]是0,取反則是1,因此爲真,awk讀取第一行後,通過++運算(先引用後賦值)將a[bbb]設置爲1,真則執行print $0,則打印第一行,第二行處理時a[bbb]爲1,取反爲0,因此爲假,awk讀取第二行後,通過++運算,將a[bbb]設置爲2,,假則不進行action,第三行處理時,a[bbb]爲2,同理不打印。
驗證:
[root@salt-node01 awk_test]# awk '{!a[$0]++;print a[$0]}' uniq.text
1
2
3
1
2
3
1
2
3
4
5
6
7
Q5:分別計算每一個字符串後面的數字總和
[root@salt-node01 awk_test]# cat uniq.text
bbb 100
bbb 100
bbb 1
ccc 88
ccc 99
ccc 765
aaa 1
aaa 2
aaa 4
aaa 5
aaa 1008
aaa 876
aaa 123
答案:
[root@salt-node01 awk_test]# awk ' {a[$1]+=$2} END {for(i in a) print i,a[i]} ' uniq.text
aaa 2019
ccc 952
bbb 201
分析:awk問題該怎麼作呢?歸根究竟是算法問題,好比在此例中,計算學生aaa的成績總和,若是有個數組就方便不少,而shell外邊向awk傳變量也是一種思路,可是來回傳遞效率低,顯得B格不夠,此處用shell數組實現就很方便,awk是C語言的編程風格,+=是先加後賦值的語法,此處等同於a[$1]=a[$1]+$2;處理第一行,a[bbb]=0+100=100,處理第二行a[bbb]=100+100=200,處理第三行a[bbb]=200+1=201,同理,能夠計算ccc bbb學生的成績總和
Q6:計算學生成績平均值及總和
要求輸出格式:(average:平均成績,total:總成績)
name#######average#######total
zhangsan            xxx                        xxx
lisi                       xxx                        xxx
wangwu             xxx                        xxx
解法:
[root@localhost awk_test]# awk -F " " 'BEGIN{print "name#######average#######total"} {sum[$1]+=$2;total[$1]++} END {for (i in sum) print i,sum[i]/total[i],sum[i]}' 01.txt
name#######average#######total
zhangsan 85 255
wangwu 92.6667 278
lisi 87.1667 261.5
分析:此處BEGIN{}輸出name等表頭信息;{}用於域值的處理;END{}用for循環輸出信息
Q7:假如把3列和4列的和值做爲新的第5列,第5列的平均值爲avg5,求第5列中大於avg5的行數
[root@localhost awk_test]# more 02.text
4 6 7 8
3 4 2 1
5 6 7 10
3 4 5 5
3 3 2 1
5 6 1 10
解法:
[root@localhost awk_test]# awk '{sum+=$3+$4;array[NR]=$3+$4}END{arg=sum/NR;for (i=1;i<=length(array);i++) {if (array[i]>arg) print i}}' 02.text
1
3
4
6
小結:
for語句和if語句進行循環處理時候,須要用()把條件進行包圍,語法是C語言風格
if (expression) { statement; statement; ... ... }
此處是for語句的statement中嵌套了一個if語句
Q8:處理文件
02.text文件是
[root@salt-node01 awk_test.bak]# more 02.text
1.1.1.1 11
1.1.1.1 22
1.1.1.1 33
1.1.1.1 44
2.2.2.2 11
2.2.2.2 22
2.2.2.2 33
2.2.2.2 44
awk命令及輸出結果:
[root@salt-node01 awk_test]# awk -F " " 'BEGIN{}{a[$1]=a[$1]" "$2}END{for (i in a){print i,a[i]}}' 02.text
1.1.1.1 11 22 33 44
2.2.2.2 11 22 33 44
分析:觀察每次行讀取處理後每行值的變化狀況,第一次是1.1.1.1 11 第二次是1.1.1.1 11 22 ......應該是這樣,用shell數組配合自增會是一個perfect的解決方案,array[$1]=array[$1]""[$2]
09:按照第三列進行排序:
[root@salt-node01 awk_test]# cat 03.text
12 34 56
78 90 12
34 56 89
方法01:sort命令
[root@salt-node01 awk_test]# cat 03.text |sort -t ' ' -k 3
78 90 12
12 34 56
34 56 89
方法02:awk方法
對於兩個field的文件,用awk內置的asorti函數能夠實現排序,三個field的文件,還須要進一步考慮,有時間看看。
10:鏈接2個文件,文件內容是:
[root@salt-node01 awk_test]# cat join.0*
100 wangyp
200 jack
300 tom
100 1000$
200 1010$
300 1100$
方法01:join
[root@salt-node01 awk_test]# join -a 1 join.01 join.02
100 wangyp 1000$
200 jack 1010$
300 tom 1100$
方法02:awk
[root@salt-node01 awk_test]# awk 'ARGIND==1{a[$1]=$0};ARGIND==2{print a[$1],$2}' join.01 join.02
100 wangyp 1000$
200 jack 1010$
300 tom 1100$
方法03:paste
[root@salt-node01 awk_test]# paste join.01 join.02 | awk '{print $1,$2,$NF}'
100 wangyp 1000$
200 jack 1010$
300 tom 1100$
方法04:
[root@salt-node01 awk_test]# awk 'NR==FNR{a[$1]=$0};NR>FNR{print a[$1],$2}' join.01 join.02
100 wangyp 1000$
200 jack 1010$
300 tom 1100$
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息