注意:本文並不是一篇awk入門文章,而是偏重實例講解
awk借鑒了c語法,因此awk在許多地方還保留有c語言的痕跡,比如printf語句;for,if的語法結構等
最簡單地說,AWK 是一種用於處理文本的編程語言工具,處理模式是只要在輸入數據中有模式匹配,就執行一系列指令。awk命令格式為:
awk {pattern + action} {filenames}awk可以讀取後接的文件,也可以讀取來自前一命令的標准輸入,它分別掃描輸入數據的每一行,查找命令行中pattern是否匹配。如果匹配,則進行後續動作action。如果pattern不匹配或action部分處理完畢,則繼續處理下一行,直到結束
相比於sed常常作用於一整行的處理,awk則比較傾向於將一行分成數個字段來處理。awk將輸入數據視為一個文本數據庫,像數據庫一樣,它也有記錄和字段的概念。默認情況下,記錄的分隔符是回車,字段的分隔符是空白符(空格,\t),所以輸入數據的每一行表示一個記錄,而每一行中的內容被空白分隔成多個字段。利用字段和記錄,awk可以非常靈活地處理文件
一個典型的awk語法如下:
awk '{ BEGIN{stat1} BEGIN{stat2}其中BEGIN為處理文本前的操作,一般用於改變FS,OFS,RS,ORS等,BEGIN部分完成之後,awk讀取第一行輸入,並將第一行的數據填入0, 0, 1,$2,NR,NF等變量,然後進入正式處理階段,待所有行處理完畢之後,進入END部分,END一般用於總結,打印報表等。正式處理是一個內建的循環,每一次循環讀取一行數據,每一行的處理分為多模式,多動作,文本行符合條件pattern1就執行動作action1符合pattern2就執行動作action2…,還可以有默認的動作, 即沒有pattern判斷,始終執行此{}內的action。 BEGIN,END部分不是必須出現,可以沒有,也可以有任意多個 pattern部分的寫法有:
pattern1{action1} pattern2{action2} ... patternn{actionn} {默認動作,無條件,始終執行} END{stat1} END{stat2} }'
pattern,部分和隨後的if,for部分,能用到的符號有:
$0 當前記錄(這個變量中存放著整個行的內容) $1~$n 當前記錄的第n個字段,字段間由FS分隔 FS 輸入字段分隔符 默認是空格或\t NF 當前記錄中的字段個數,就是有多少列 NR 已經讀出的記錄數,就是行號,從1開始,如果有多個文件話,這個值也是不斷累加中。 FNR 當前記錄數,與NR不同的是,這個值會是各個文件自己的行號 RS 輸入的記錄分隔符, 默認為換行符 OFS 輸出字段分隔符, 默認也是空格 ORS 輸出的記錄分隔符,默認為換行符 FILENAME 當前輸入文件的名字
#在任何時候{}內都可以跟多個並列動作(使用“;”分隔),下面的{action1} 和 {action1;action2;...} 都表示{}體內有多個動作,兩種表示沒有任何區別,寫第二種僅僅是為了直觀的表示可以有多個動作
#for循環寫法
for(i=1;i<=NF;i++){action1; action2; ..} #{}中用分號分隔多個動作 for(i=1;i<=NF;i++)if; else if;else #for後接一個if結構 for(i=1;i<=NF;i++)printf “for add” #簡單的循環打印
#if 判斷寫法
if($1 ~ /reg/){action1}; else if($1 ~ /reg2/){action2}; else{action3} #else if部分可以沒有 if($1 ~ /reg/ && $2 ~ /reg2/){action} #多個條件用”&&”,”||”表示 if($1 ~ /reg/ || NR >= 5){action
# if,for 混合寫法
{ for(i=1;i<=NF;i++)if(…) printf “test”; else if(…) printf “test2”; else printf “test3”; print "not_for" }for循環的作用范圍為:
#print “not_for”部分是並列與for循環結構的另一個action,在for循環之外,只會打印一次 { for(i=1;i<=NF;i++){if(…) printf “test”; else if(…) printf “test2”; else printf “test3”;print “in_for“}; print "not_in_for" } { for(i=1;i<=NF;i++){if {s1;s2;} else if {s3;s4;} else {s5;s6;}; print "test"} } #else if前不加分號 { for(i=1;i<=NF;i++)printf "for_add"; if(…);else if(…); else } #if並不在for循環體內
if語句的作用范圍:
1: AWK使用的RE為ERE
2: 如果在BEGIN中設置了OFS, 只有$0有改動OFS才能生效
3: printf 與 print 的區別: printf 不自動打印換行符, print 則自動打印
4: gsub的返回值並不是替換後的字符串,而是返回替換的次數
5: 字符串常量一定在用" "包圍起來,否則當作變量使用, 如 $1=="ipaddress"
6: AWK 的 for 循環為 C-Style,即為 for(), 區別於shell中的for i in ...
7: AWK中可以使用多個分隔符,要封裝在方括號裡,用' '包圍,以防 shell 對它們進行解釋,如 awk -F '[ :/t]' ,使用空格,冒號,tab作為分隔符
8: next語句:從輸入文件中取得下一個輸入行,在AWK命令表頂部重新執行命令,一般用於跳過一些特殊的行
9: awk 匹配多個條件: awk '/kobe/ && /james/' #匹配同時有kobe和james的行
10: FS的默認值是[ /t/n]+, OFS的默認值為空格,RS,ORS的默認值都是換行
11: 定位行有兩種方法: 1: NR==行號 2: 用RE /Love$/
12: exit語句:終止AWK程序,但不跳過END語句
13:1.. 1.. n表示第幾列(字段),$0表示整個行.
14:awk可用比較運算符:!=, >, <, >=, <=
15: 字符串匹配:~: 匹配 !~: 不匹配
16: &&:多個條件且, || 多個條件或
17: {s1;s2;s3;...}中多個語句用分號隔開;if; else if; else
18: print 後不帶任何參數時,相當於print $0 ,將會打印整行記錄
awk '/AL/ {printf $1; print $2}' emp.txt awk '/AL/{print $1} {print $2}' emp.txt
1.第一種只處理匹配到AL的行; 然後打印這些行的第一字段和第二字段
2.第二種只有在匹配到AL的行才打印字段一,但是字段二是無條件的,始終打印
如下文本test.log,需要把文本中單位是"M"的字段轉換為"G"
16G 16G 1.9G 40G none 4G 4G 952M 60G 16G 16G 1.6G 40G none 5G 780M 5G 80G
若我們單純地想替換字符串,則可以使用一下命令,注意:print沒帶默認參數時,默認打印整行記錄
cat 1.txt | awk '{
sub(/M/,"G",$i)
}'
完整替換如下,替換的同時進行數值計算
cat 1.log | \ awk '{ for(i=1;i<=NF;i++){ if($i ~ /M/) printf int(substr($i,1,match($i,/M/)-1)/1024*100)/100"G\t" else printf $i"\t" } print "" }'
1.使用for遍歷每一行的每一個字段,使用if處理匹配到"M"的字段,然後調用awk內置函數int, substr, match處理, 關於awk內置函數,可以參考Linux awk 內置函數詳細介紹(實例) http://www.linuxidc.com/Linux/2012-11/74816.htm
2.先乘100,再除以100,是為了轉換為"G"單位後保留兩位小數
3.注意 printf,print 兩個函數的區別
如第二個例子中的文本,現在需要分別統計每行中帶有"G", "M", "none" 字段的個數,並輸出
cat test.log | \ awk ' BEGIN{OFS="\t"} { a=b=c=0 for(i=1;i<=NF;i++){ if($i ~ /G/) a+=1 else if($i ~ /M/) b+=1 else c+=1 } print $0,"G:"a,"M:"b,"none:"c } END{print "end!"} '
結果如下:
16G 16G 1.9G 40G G:4 M:0 none:0 4G 4G 952M 60G G:3 M:1 none:0 16G 16G 1.6G 40G G:4 M:0 none:0 5G 780M 5G 80G G:3 M:1 none:0 end!
給出一個如下文本test2.log,在每一行中,只輸出字母及其之前的字符
1 2 a 5 6 11 b 55 66 21 22 23 c 25 26
寫法如下:
cat test2.log | \ awk '{ for(i=1;i<=NF;i++){ if($i ~ /[a-z]/) { printf $i"\t" break } else printf $i"\t" } print "" }'
1.break 用法跟c語言用法一樣,跳出for循環
如下文本test3.log,給定一個id值,輸出其在所有id中是第幾個
test1
id=615187629 test2
test3
id=615183434 test4
id=615123789 test5
id=615975882
給定id值615123789,其在所有id中是第三個,計算如下:
cat test3.log | \ awk -v var1=615123789 -F [=] ' /id/ { b+=1 a[b]=$2 } END { for(i in a) if(a[i] == var1) print "number:",i } '
6.awk中的整數計算
如下文本,這是一個kvm虛擬機進程(省略了部分文本),我們要獲取其映射到宿主機上的vnc端口號,即由"-vnc 0.0.0.0:1"字符串計算出其vnc端口號為5901(5900 + 1),若是"-vnc 0.0.0.0:2",則端口號為5902
cat kvm.txt
qemu 144148 4.7 4.2 ... /usr/local/qemu/bin/qemu-kvm -name lnmptest-107 ... -device isa-serial,chardev=charserial0,id=serial0 -vnc 0.0.0.0:1 -vga cirrus timestamp=on
實現有多種,sed,shell,awk都可以
#awk
cat kvm.txt | awk '{ for(i=1;i<=NF;i++){if($i == "-vnc"){sub(/0.0.0.0:/,"",$(i+1));print $(i+1)+5900}} }'
#shell
cat kvm.txt | egrep -o "\-vnc [^ ]*" | awk -F: '{print $2+5900}'
#sed
cat kvm.txt | sed "s/^.* -vnc [0-9.]*:\([0-9]*\).*/\1+5900/g" | bc
Linux系統之文本格式化工具awk http://www.linuxidc.com/Linux/2016-02/128150.htm
AWK簡介及使用實例 http://www.linuxidc.com/Linux/2013-12/93519.htm
Linux awk文本分析工具 http://www.linuxidc.com/Linux/2015-12/126217.htm
Linux文本處理工具之awk http://www.linuxidc.com/Linux/2015-01/111437.htm
如何在Linux中使用awk命令 http://www.linuxidc.com/Linux/2014-10/107542.htm
文本分析工具-awk http://www.linuxidc.com/Linux/2014-12/110939.htm