首頁 > 軟體

awk基本用法和工作原理詳解

2020-06-16 17:06:05

目錄

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


IT145.com E-mail:sddin#qq.com