2021-05-12 14:32:11
awk基本用法和工作原理詳解
目錄
1.awk介紹
2.awk基本用法和工作原理
3.awk的運用說明
1.awk介紹
awk是一種報表生成器,就是對檔案進行格式化處理的,這裡的格式化不是檔案系統的格式化,而是對檔案內容進行各種“排版”,進而格式化顯示。
在linux之上我們使用的是GNU awk簡稱gawk,並且gawk其實就是awk的連結檔案,因此在系統上使用awk和gawk是一樣。
通過man awk可以取得相關功能說明,還可以知道,gawk是一種程序式程式設計語言,支援條件判斷、陣列、迴圈等各種程式語言中所有可以使用的功能,因此我們還可以把awk稱為一種指令碼語言直譯器。
2.awk基本用法和工作原理
gawk - pattern scanning and processing language
:(模式掃描和處理語言)
基本用法:
格式1:
awk [options] -f progfile [--] file ...
格式2:awk [options] [--] 'program' file ...
格式3awk [options] 'BEGIN{ action;… } pattern{ action;… } END{ action;… }' file ...
-f progfile,--file=progfile
:從檔案中來讀取awk 的program-F fs,--field-separator=fs
:指明輸入時用到的欄位分割符-v var=val,--assign=var=val
:在執行program之前來定義變數program
:相當於程式語言,也就是處理後面檔案的一系列操作語句progfile
:帶有program或BEGIN等操作語句內容的檔案BEGIN
:讀取輸入流前進行操作的標誌END
:輸入流讀取完後進行操作的標誌pattern
:模式,對輸入流進行操作,實際上paogram就代表這pattern部分action
:動作語言,由多種語句組成,語句間用分號分割
工作原理:
從上面可以看到看似有三個格式,實際上總的來說就一個格式,就是格式3,因為格式1和2展開後,也就是格式3。
格式:
awk [options] 'BEGIN{ action;… } pattern{ action;… } END{ action;… }' file ...
第一步:執行[option]相關內容,也就是-f,-F,-v
選項內容。
第二步:執行BEGIN{action;… } 語句塊中的語句。BEGIN 語句塊在awk開始從輸入流中讀取行之前被執行,這是一個可選的語句塊,比如變數初始化、列印輸出表格的錶頭等語句通常可以寫在BEGIN 語句塊中。
第三步:從檔案或標準輸入(stdin) 讀取每一行,然後執行pattern{action;… }語句塊,它逐行掃描檔案,從第一行到最後一行重複這個過程,直到檔案全部被讀取完畢。pattern語句塊中的通用命令是最重要的部分,也是可選的。如果沒有提供pattern 語句塊,則預設執行{ print } ,即列印每一個讀取到的行,awk 讀取的每一行都會執行該語句塊。
第四步:當讀至輸入流末尾時,也就是所有行都被讀取完執行完後,再執行END{action;…} 語句塊。END 語句塊在awk從輸入流中讀取完所有的行之後即被執行,比如列印所有行的分析結果這類資訊彙總都是在END 語句塊中完成,它也是一個可選語句塊。
3.awk的運用
1.awk中的變數
變數分為內建變數和自定義變數,但只要是變數都是用的選項-v
。先選常用的內建變數說明下,然後說下自定義的變數。
內建變數:
FS
:輸入欄位分隔符,預設為空白字元,這個想當於-F
選項。分隔符可以是多個,用[]括起來表示,如:-v FS="[,./-:;]"
OFS
:輸出欄位分隔符,預設為空白字元,分隔符可以是多個,同上RS
:輸入記錄(所認為的行)分隔符,指定輸入時的換行符,原換行符仍有效,分隔符可以是多個,同上ORS
:輸出記錄(所認為的行)分隔符,輸出時用指定符號代替換行符,分隔符可以是多個,同上NF
:欄位數量NR
:記錄數(所認為的行)FNR
:各檔案分別計數, 記錄數(行號)FILENAME
:當前檔名ARGC
:命令列引數的個數ARGV
:陣列,儲存的是命令列所給定的各引數
自定義變數(區分字元大小寫):
在'{...}'前,需要用-v var=value:
awk -v var=value '{...}'
在program 中直接定義:awk '{var=vlue}'
下面針對每個變數都舉個例子:
awk -v FS=':' '{print $1,FS,$3}’ /etc/passwd
awk –F: '{print $1,$3,$7}’ /etc/passwd
awk -v FS=‘:’ -v OFS=‘:’ '{print $1,$3,$7}’ /etc/passwd
awk -v RS=' ' ‘{print }’ /etc/passwd
awk -v RS="[[:space:]/=]" '{print }' /etc/fstab |sort
awk -v RS=' ' -v ORS='###'‘{print }’ /etc/passwd
awk -F: ‘{print NF}’ /etc/fstab, 參照內建變數不用$
awk -F: '{print $(NF-1)}' /etc/passwd
awk '{print NR}' /etc/fstab ; awk 'END{print NR}' /etc/fstab
awk '{print FNR}' /etc/fstab /etc/inittab
awk '{print FNR}' /etc/fstab /etc/inittab
awk '{print FILENAME}’ /etc/fstab
awk '{print ARGC}’ /etc/fstab /etc/inittab
awk ‘BEGIN {print ARGC}’ /etc/fstab /etc/inittab
awk ‘BEGIN {print ARGV[0]}’ /etc/fstab /etc/inittab
awk ‘BEGIN {print ARGV[1]}’ /etc/fstab /etc/inittab
awk -v test='hello gawk' '{print test}' /etc/fstab
awk -v test='hello gawk' 'BEGIN{print test}'
awk 'BEGIN{test="hello,gawk";print test}'
awk –F:‘{sex=“male”;print $1,sex,age;age=18}’ /etc/passwd
awk -F: '{sex="male";age=18;print $1,sex,age}' /etc/passwd
echo "{print script,$1,$2}" > awkscript
awk -F: -f awkscript script=“awk” /etc/passwd
2.awk的print和printf
print和printf都是列印輸出的,不過兩者用法和顯示上有些不同而已。
print 格式:
print item1,item2, ...
printf格式:printf “FORMAT ”,item1,item2, ...
要點:
1.逗號為分隔符時,顯示的是空格;
2.分隔符分隔的欄位(域)標記稱為域標識,用$0,$1,$2,...,$n表示,其中$0 為所有域,$1就是表示第一個欄位(域),以此類推;
3.輸出的各item可以字串,也可以是數值,當前記錄的欄位,變數或awk 的表示式等;
4.如果省略了item ,相當於print $0
5.對於printf來說,必須指定FORMAT,即必須指出後面每個itemsN的輸出格式,且還不會自動換行,需要顯式則指明換行控制符"n"
printf的格式符和修飾符:
%c
:顯示字元的ASCII碼%d, %i
:顯示十進位制整數%e, %E
:顯示科學計數法數值%f
:顯示為浮點數%g, %G
:以科學計數法或浮點形式顯示數值%s
:顯示字串%u
:無符號整數%%
:顯示%自身#[.#]
:第一個數位控制顯示的寬度;第二個#表示小數點後精度,%3.1f-
:左對齊(預設右對齊);%-15s,就是以左對齊方式顯示15個字元長度+
:顯示數值的正負符號 %+d
這裡也舉個範例:
awk '{print "hello,awk"}'
awk –F: '{print}' /etc/passwd
awk –F: ‘{print “wang”}’ /etc/passwd
awk –F: ‘{print $1}’ /etc/passwd
awk –F: ‘{print $0}’ /etc/passwd
awk –F: ‘{print $1”t”$3}’ /etc/passwd
tail –3 /etc/fstab |awk ‘{print $2,$4}’
awk -F: ‘{printf "%s",$1}’ /etc/passwd
awk -F: ‘{printf "%sn",$1}’ /etc/passwd
awk -F: '{printf "%-20s %10dn",$1,$3}' /etc/passwd
awk -F: ‘{printf "Username: %sn",$1}’ /etc/passwd
awk -F: ‘{printf “Username: %s,UID:%dn",$1,$3}’ /etc/passwd
awk -F: ‘{printf "Username: %15s,UID:%dn",$1,$3}’ /etc/passwd
awk -F: ‘{printf "Username: %-15s,UID:%dn",$1,$3}’ /etc/passwd
lsmod
awk -v FS=" " 'BEGIN{printf "%s %26s %10sn","Module","Size","Used by"}{printf "%-20s %13d %5s %sn",$1,$2,$3,$4}' /proc/modules
3.awk的操作符
算術操作符:
x+y, x-y, x*y, x/y, x^y, x%y
賦值操作符:=, +=, -=, *=, /=, %=, ^=,++, --
比較操作符:==, !=, >, >=, <, <=
模式匹配符:~ :左邊是否和右邊匹配包含;!~ :是否不匹配
邏輯操作符:與:&& ;或:|| ;非:!
條件表示式(三目表示式):selector ? if-true-expression : if-false-expression
範例:
awk –F: '$0 ~ /root/{print $1}‘ /etc/passwd
awk '$0~“^root"' /etc/passwd
awk '$0 !~ /root/‘ /etc/passwd
awk –F: ‘$3==0’ /etc/passwd
awk–F: '$3>=0 && $3<=1000 {print $1}' /etc/passwd
awk -F: '$3==0 || $3>=1000 {print $1}' /etc/passwd
awk -F: ‘!($3==0) {print $1}' /etc/passwd
awk -F: ‘!($3>=500) {print $3}’ /etc/passwd
awk -F: '{$3>=1000?usertype="Common User":usertype="Sysadmin or SySUSEr";printf "%15s:%-sn",$1,usertype}' /etc/passwd
4.awk的pattern
awk語句中是根據pattern條件,過濾匹配的行,再做處理。
1.未指定:表示空模式,匹配每一行
2./regular expression/
:僅處理能夠模式匹配到的行,支援正規表示式,需要用/ /
括起來
3.關係表示式:結果為“真”才會被處理。真:結果為非0值,非空字串。假:結果為空字串或0值
4./pat1/,/pat2/
:startline,endline ,行範圍,支援正規表示式,不支援直接給出數位格式
5.BEGIN{}和END{}
:BEGIN{}
僅在開始處理檔案中的文字之前執行一次。END{}
僅在文字處理完成之後執行 一次
範例:
awk '/^UUID/{print $1}' /etc/fstab
awk '!/^UUID/{print $1}' /etc/fstab
awk -F: ‘/^root>/,/^nobody>/{print $1}' /etc/passwd
awk -F: ‘(NR>=10&&NR<=20){print NR,$1}' /etc/passw
awk -F: 'i=1;j=1{print i,j}' /etc/passwd
awk ‘!0’ /etc/passwd ; awk ‘!1’ /etc/passwd
awk –F: '$3>=1000{print $1,$3}' /etc/passwd
awk -F: '$3<1000{print $1,$3}' /etc/passwd
awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd
awk -F: '$NF ~ /bash$/{print $1,$NF}' /etc/passwd
awk -F : 'BEGIN {print "USER USERID"} {print $1":"$3}END{print "end file"}' /etc/passwd
awk -F: 'BEGIN{print " USER USERID"}{printf "|%8s| %10d|n",$1,$3}END{print "END FILE"}' /etc/passwd
awk -F : '{print "USER USERID“;print $1":"$3} END{print"end file"}' /etc/passwd
awk -F: 'BEGIN{print " USER UID n---------------"}{print $1,$3}' /etc/passwd
awk -F: 'BEGIN{print " USER USERIDn----------------------"}{printf "|%8s| %10d|n",$1,$3}END{print "----------------------nEND FILE"}' /etc/passwd
awk -F: 'BEGIN{print " USER UID n---------------"}{print $1,$3}'END{print "=============="} /etc/passwd
seq 10 |awk ‘i=0’
seq 10 |awk ‘i=1’
seq 10 | awk 'i=!i‘
seq 10 | awk '{i=!i;print i}‘
seq 10 | awk ‘!(i=!i)’
seq 10 |awk -v i=1 'i=!i'
5.awk的action
awk中的action可以分為以下5類:
1.表示式語句,包括算術表示式和比較表示式,就是用進行比較和計算的。
2.控制語句,用作進行控制,典型的就是if else,while等語句,和bash指令碼裡面用法差不多。
3.輸入語句,用來做為輸入,變數賦值就算是。
4.輸出語句,用來輸出顯示的,典型的是print和printf
5.組合語句,這個很多理解,就是多種語句的組合
下面就具體說下一些語句的具體內容,也沒有分割那麼清晰,但都屬於action範圍:
1.awk的if-else
語法:
{if(condition){statement;…}}
:條件滿足就執行statement{if(condition){statement1;…}{else statement2}}
:條件滿足執行statement1,不滿足執行statement2{if(condition1){statement1}else if(condition2){statement2}else{statement3}}
:條件1滿足執行statement2,不滿足條件1但滿足條件2執行statement2,所用條件都不滿足就執行statement3
範例:
awk -F: '{if($3>=1000)print $1,$3}' /etc/passwd
awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd
awk '{if(NF>5) print $0}' /etc/fstab
awk -F: '{if($3>=1000) {printf "Common user: %sn",$1}else{printf "root or Sysuser: %sn",$1}}' /etc/passwd
awk -F: '{if($3>=1000) printf "Common user: %sn",$1;else printf "root or Sysuser: %sn",$1}' /etc/passwd
df -h|awk -F% '/^/dev/{print $1}'|awk '$NF>=80{print $1,$5}‘
awk 'BEGIN{ test=100;if(test>90){print "very good"}else if(test>60){ print "good"}else{print "no pass"}}'
2.awk的while和do-while
語法:
while(condition){statement;…}
:條件為“真”時,進入迴圈;條件為“假”時, 退出迴圈do {statement;…}while(condition)
:無論真假,至少執行一次迴圈體。當條件為“真”時,退出迴圈;條件為“假”時,繼續迴圈
範例:
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
awk 'BEGIN{ total=0;i=0;do{total+=i;i++}while(i<=100);print total}‘
3.awk的for
語法:
for(expr1;expr2;expr3) {statement;…}
:expr1為變數賦值,如var=value,初始進行變數賦值;expr2為條件判斷語句,j<=10,滿足條件就繼續執行statement;expr3為疊代語句,如j++,每次執行完statement後就疊代增加for(var in array) {for-body}
:變數var遍歷陣列,每個陣列中的var都會執行一次for-body
範例:
awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg
awk '/^[^#]/{type[$3]++}END{for(i in type)print i,type[i]}' /etc/fstab
awk -v RS="[[:space:]/=,-]" '/[[:alpha:]]/{ha[$0]++}END{for(i in ha)print i,ha[i]}' /etc/fstab
4.awk的switch
switch 語句,相當於bash中的case語句。
語法:
switch(expr) {case VAL1 or /REGEXP/:statement1; case VAL2 or /REGEXP2/: statement2;...; default: statementn}
:若expr滿足 VAL1 or /REGEXP/就執行statement1,若expr滿足VAL2 or /REGEXP2/就執行statement2,以此類推,執行statementN,都不滿足就執行statement
5.awk的break、continue和next
break 和continue,用於條件判斷迴圈語句,next是用於awk自身迴圈的語句。
break[n]
:當第n次迴圈到來後,結束整個迴圈,n=0就是指本次迴圈continue[n]
:滿足條件後,直接進行第n次迴圈,本次回圈不在進行,n=0也就是提前結束本次迴圈而直接進入下一輪next
:提前結束對本行的處理動作而直接進入下一行處理
範例:
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}‘
awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd
6.awk的陣列
awk的陣列是關聯陣列,格式為:
array[index-expression]
:arry為陣列名,index-expression為下標。
實際上index-expression可使用任意字串,字串要使用雙引號括起來;如果某陣列元素事先不存在,在參照時,awk 會自動建立此元素,並將其值初始化為“空串”。
若要判斷陣列中是否存在某元素,要使用“index in array”格式進行遍歷。
若要遍歷陣列中的每個元素,要使用for迴圈:for(var in array) {for-body}
,使用for迴圈會使var 會遍歷array的每個索引。此時要顯示陣列元素的值,則要使用array[var]。
範例:
awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";print weekdays["mon"]}‘
awk '!arr[$0]++' dupfile
awk '{!arr[$0]++;print $0, arr[$0]}' dupfile
awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {print weekdays[i]}}‘
netstat -tan | awk '/^tcp/{state[$NF]++}END{for(i in state) { print i,state[i]}}'
awk '{ip[$1]++}END{for(i in ip) {print i,ip[i]}}'/var/log/httpd/access_log
7.awk的函數
awk的函數有許多,除了系統自帶的內建函數還有就是使用者自定義的函數,這裡挑選幾個常???的函數。
rand()
:返回0 和1 之間一個亂數srand()
:生成亂數種子int()
:取整數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,…也就是說awk
的陣列下標是從1開始編號的。substr(s,i,[n])
:從s
所表示的字串中取子串,取法:從i
表示的位置開始,取n
個字元。systime()
:取當前系統時間,結果形式為時間戳。system()
:呼叫shell中的命令。空格是awk中的字串連線符,如果system 中需要使用awk中的變數可以使用空格分隔,或者說除了awk的變數外其他一律用"" 參照 起來。
範例:
awk 'BEGIN{srand(); for (i=1;i<=10;i++)print int(rand()*100) }'
echo "2008:08:08 08:08:08" | awk 'sub(/:/,“-",$1)'
echo "2008:08:08 08:08:08" | awk ‘gsub(/:/,“-",$0)'
netstat -tan | awk '/^tcp>/{split($5,ip,":");count[ip[1]]++}END{for (i in count) {print i,count[i]}}'
awk BEGIN'{system("hostname") }'
awk 'BEGIN{score=100; system("echo your score is " score) }'
自定義函數,格式為
function fname ( arg1,arg2 , ... ) {
statements
return expr
}
自定義函數中fname為函數名,arg1...為函數的引數,statements是動作語言,return expr為由statements的結果從而決定最終函數所顯示的內容。
範例:
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
6.awk的指令碼
awk的指令碼就是將awk程式寫成指令碼形式,來直接呼叫或直接執行。例如上面寫自定義函數的樣子也算是指令碼。
格式1:
BEGIN{} pattern{} END{}
格式2:
#!/bin/awk -f
#add 'x' right
BEGIN{} pattern{} END{}
格式1假設為f1.awk檔案,格式2假設為f2.awk檔案,那麼用法是:
awk [-v var=value] f1.awk [file]
f2.awk [-v var=value] [var1=value1] [file]
對於awk [-v var=value] f1.awk [file]
來說,很好理解,就是把處理階段放到一個檔案而已,真正展開後,也就是普通的awk語句。
對於f2.awk [-v var=value] [var1=value1] [file]
來說, [-v var=value]
是在BEGIN之前設定的變數值,[var1=value1]
是在BEGIN過程之後進行的,也就是說直到首行輸入完成後,這個變數才可用,這就想awk指令碼黃總傳遞引數了。
範例:
cat f1.awk
{if($3>=1000)print $1,$3}
awk -F: -f f1.awk /etc/passwd
cat f2.awk
#!/bin/awk –f
#this is a awk script
{if($3>=1000)print $1,$3}
#chmod +x f2.awk
f2.awk –F: /etc/passwd
cat test.awk
#!/bin/awk –f
{if($3 >=min && $3<=max)print $1,$3}
#chmod +x test.awk
test.awk -F: min=100 max=200 /etc/passwd
到此為止算是差不多了,實際上awk的內容好多,這裡只是寫了所知道的,更詳細的可看man awk來獲取更加全面的知識。上述若有錯誤的地方,歡迎提出!謝謝~
本文永久更新連結地址:http://www.linuxidc.com/Linux/2017-10/147270.htm
相關文章