awk用法小結

awk用法小結 - - - by ruson 2006.4 NTU
1. awk非常適合於結構化的文本文件(行、列數據)複雜處理。相對於sed而言,它可進行復雜的編程處理,並且可以產生複雜的報表輸出。
2. awk通常有三個版本,舊awk、nawk(新)、gawk。Solaris下建議用nawk,因爲舊awk有很多功能不支持,例如數學運算冪 ^ ,函數getline,system等。
3. 基本語法:awk ‘pattern{action}’ filename 其中action內容可擴充,也可以有多個action。
執行順序:awk一行行讀入輸入文件,順序執行‘’內內容,按模式匹配來採取動作。
其他調用:awk可用內部變量和函數,條件與循環語句,也可執行數學運算和字符串操作。此外,可以使用BEGIN和END來執行處理前預操作和處理後後繼操作。
A. 常用內部變量:NR(當前行數)NF(字段總數)$0(當前整行)$1(第一個記錄)
FS(字段分隔符)OFS(輸出字段分隔符)ORS(輸出記錄分隔符)
B.模式 pattern可以是/ /包含的匹配形式,也是條件語句如$3<10
C.BEGIN(處理文件前的action,常包含FS、OFS等)、END(處理文件後的action)
D.條件與循環:if else(next,exit),for do while (continue,break)
E.數學運算符 + - * / % ^;數學函數sin int;字符串函數length index gsub substr等
F.數組與關聯數組:a[1]; a[$1]; a[$0]; a[b[i]]
F.輸出重定向和管道:> >> | ;awk內部命令:getline、system等
4.awk的三種調用方式: 􀁘 awk ‘pattern{action}’ filename1,filename2
􀁙 awk –f myscript.awk filename1,filename2
􀁚 #!/bin/nawk –f … … 執行:myscript.awk filename
特別注意事項:
----------------------------------------------------------------------------------------------------------------------
A.從window拷貝語法句到Unix中,行結尾可能有不識別的字符會導致語法錯誤
B.寫script文件時,BEGIN或END後必須緊跟{
C.所有action語句必須放在{ }中,否則提示語法錯誤。例如不可直接寫print
D.單獨的賦值或pattern條件句沒有{ }則默認print $0;例如{ }外面的i=1
E.if條件塊必須放在action中,也就是必須要有{ }來包含
F.條件的判斷“相等”須用= =,而不是=(賦值);例如,if($1= =100)
G.輸出多列須用“,”(逗號)隔開;for循環中間隔符爲“;”而不是“,”
實例演練:(filename爲以下文件,注意:第二列用“-”隔開而非空格,以避免與FS衝突)
ID Name Age City Country Tel Salary Children
1001 Steven 25 NY U.S.A +01-02-323222 $4900 2
1002 Huang-Yu 30 BJ CHN +86-10-36789966 ¥6000 1
1003 Fish-Mad 27 SG SG +65-67456632 $3000 3
1004 Vale-Kiss 46 LD ENG +44-20-87634321 $6280 3
---------------------------------------------------------------------------------------------------------------------- 以下分基礎實例:1.無pattern 的action實例 2.有pattern的action實例
3.BEGIN,END,FS,OFS實例 4.if else; for while循環實例
5.數學運算與字符串操作實例
高級實例:6.數組與關聯數組運用實例 7.多文件運用NR FNR實例
8.輸出重定向和getline實例 1
基 礎 篇
1.無pattern的action實例
a.awk ‘{print NR $1 $NF}’ data.txt 打印行號,第一列和最後一列,中間無分隔符
b.awk ‘{print $1,$NF}’ data.txt 打印第一列和最後一列,並且中間有分隔符
c.awk ‘{print $0,$NF+10}’ data.txt 打印整行,並打印 最後一行加上10的結果
2.有pattern的action實例
a.awk ‘/[0-9]/’ data.txt 打印記錄中任意列包含數字0-9的行
b.awk ‘/01/||/02/’ data.txt 打印包含01或者02的行
c.awk ‘/01/,/02/’ data.txt 打印既包含01又包含02的行;等同awk ‘/01/&&/02’
d.awk ‘$1= =1001{print $2}’ data.txt 打印符合第一列等於1001的第二列
e.awk ‘$2= =”Steven”{print}’data.txt 打印符合第二列等於Steven的那些行
f.awk ‘$3>20&&$3<30’ data.txt 打印第三列在20到30之間的那些行
g.nawk ‘$3*$NF<100’ data.txt 打印第三列和最後一列乘積小於100的那些行
h.awk ‘$6~/01/{print $2}’ data.txt 打印符合僅僅第六列裏包含01那些行的第二列
i.awk ‘NR>3{print $1,$2}’ data.txt 從第四行纔開始打印第一列和第二列
3.有BEGIN、END幷包含FS、OFS的實例
a.awk –F”+” ‘{print $1}’ data.txt 按+爲分隔符來分隔列,並打印第一列
b.nawk –F’[+\t$]’ ‘{print $6,$7,$8,$9}’data.txt 按+或\t或$都可作分隔符,並打印指定列
c.nawk ‘BEGIN{FS=”[\t+$]”}{print $6,$8,$9}’data.txt
按+,\t,$(順序無所謂)爲分割符,並打印指定列
d.nawk ‘BEGIN{FS=”[\t+$]”;OFS=”%”}{print $6,$7,$8,$9}’ data.txt
按+,\t,$爲分割符,用%作輸出分隔符打印指定列
e.nawk –F‘[ [ ] ]’ ‘{print $1}’ data.txt 按 [ 或 ] 爲分隔符,並打印第一列
f.nawk –F‘[ ][ ]’ ‘{print $1}’ data.txt 同上,按[ 或 ] 爲分隔符,並打印第一列
關於-F和BEGIN內的FS定義特別注意如下:
A.-F參數後緊跟單個分隔符,則用雙引號“”,例如 –F”+”
B.-F參數後緊跟多個分隔符,則用單引號‘ ’並用[ ],中間順序無所謂,例如-F’[+$]’
C.BEGIN中的FS無論是單個還是多個分隔,均用雙引號“”,多個則需加[ ]
例如:nawk ‘BEGIN{FS=”[\t+$]”}{print $6,$8,$9}’data.txt
D.BEGIN中若同時有FS和OFS,則建議中間用分號“;”來分隔開,否則提示出錯
例如:nawk ‘BEGIN{FS=”[\t+$]”;OFS=”%”}{print $6,$7,$8,$9}’ data.txt
E.半方括號[ ]也可以作爲分隔符,順序無所謂。例如:-F‘[[]]’ 或 –F‘[][]’
F.在用多個分隔符的時候,爲避免語法錯誤或想得到正確的結果,最好用nawk
4.條件if else和for while循環實例
a.nawk ‘{print($3>25 ? $2“ Old” : $2“ Young”)}’ data.txt
若第三列大於25就打印某某Old;否則打印某某Young
b.awk ‘{if($1>1002)print $2; else print $3}’ data.txt
若第二列大於1002就打印第二列,否則打印第三列
c.awk ‘NR>1{if($3<30)print$2;else if($3<40) print$2”m”;else exit;}END{print 10}’data.txt
從第二行開始判斷第三列的值,如果小於30就打印$2,如果大於30而又小於
2
40,打印$2並後跟m,若是都不符合就退出處理行,但是END後的操作仍執行
d.awk ‘BEGIN{ORS=””}{for(i=1;i<NF-2;i++) print $i”\t”}{print “\n”}’ data.txt
打印出每行的前面幾列,最後幾列不打印。特別注意:默認情況下,print每執行一次時,另起一行打印。因此爲避免每打印一列就重新一行,設置ORS爲空,但是在每打印完符合條件的所有列後,需要手工換行(循環外的print起此作用)。
e.awk ‘BEGIN{ORS=””}{i=1; while(i<NF-2){print $i”\t”; i++}}{print “\n”}’ data.txt
功能同上,打印出每行的前面幾列,最後幾列不打印。
5.數學運算和字符串操作實例
a.nawk ‘{print 2^5+sin(2.1)+int(0.9)}’ data.txt 在每一行都打印運算值(32.8632)
b.awk ‘END{print length(“How are you?”)}’ data.txt 打印出字符串的長度(12)
c.awk ‘END{print index(“How are you?”,”you”)}’ data.txt 返回you在字符串中的開始位置(9)
d.nawk ‘{gsub(/ \$/,””);print $0}’ data.txt 把每行的$符號用空替掉,並打印行
e.awk ‘END{print substr(“How are you?”,9,3)}’ data.txt
從字符串的第九個位置開始截出後面3個長度的子字符串
f.nawk ‘{print toupper($2), tolower($2)}’ data.txt
分別打印第二列大寫和小寫
g.nawk ‘END{print match(“How are you you?”,/you/),RSTART,RLENGTH}’ data.txt
打印you在字符串中第一個匹配的位置以及長度(9,9,3)
h.nawk ‘END{str=”How are you doing?”;sub(/o/,”0”,str);print str}’ data.txt
將字符串變量指定字符中第一個符合含有o的用0替換調
特別注意:sub函數的第三個參數不可直接用字符串,而必須用字符串變量。
i.awk ‘END{print split(“Jan,Feb,Mar,Apr,May”,mymonths,”,”),mymonths[2]}’ data.txt
將字符串用第三個參數分隔符號進行分隔,把分隔的各個元素放在第二個參數的數組中,函數返回分隔得到的元素數目。結果:(5,Feb)
高 級 篇
6.數組與關聯數組(a[1],a[$1] a[$0],a[b[i]])
a.awk ‘{for(i=1;i<NF;i++) {a[i]=$i; print a[i]}}’ data.txt 把文件每一列按行打印
b.awk ‘{a[$1]=$2}END{for(x in a) print a[x]}’ data.txt
數組a關聯到第一列,並將第二列值賦給a,最後打印a中的所有內容。注意:但不是順序打印。for中的x是隨意變量,awk自動確定。
c.awk ‘{b[i]=$1;a[$1]=$2;i++}END{for(j=1;j<i;j++) print b[j],a[b[j]]}’ data.txt
實現從第二行開始打印每一行的第一列和第二列的功能。
注意:i從0開始取值,而j從1開始。a爲關聯數組,b爲順序數組。
7.多輸入文件和NR、FNR的實例
a. awk ‘BEGIN{OFS=FS=”:”} NR==FNR{a[$1]=$2} NR>FNR{$2=a[$1];print}’
/etc/shadow /etc/passwd
該語句中第一個模式匹配到第一個輸入文件shadow,第二個模式匹配到第二個輸入文件passwd,並使用關聯數組,實現不同文件關聯列的其他列值相互替換。 3
A.awk對多輸入文件的執行順序是,先將代碼作用於第一個文件(一行行讀入),然後該重複的代碼又作用於第二個文件,再作用於第三個文件。
B.awk對多輸入文件的執行順序產生了行序號的問題。當第一個文件執行完,下次讀入第二個文件,那麼第二個文件的第一行怎麼算呢?如果又計爲1的話,那不就兩個1了麼?(因爲第一個文件也有第一行)。這就是NR和FNR的問題。
NR :全局行數(第二個文件的第一行接着第一個文件尾行數順序計數)
FNR:當前文件自身的行數(不考慮前幾個輸入文件的自身行數及總數)
例如:data1.txt中有40行,data2.txt中有50行,那麼awk ‘{}’ data1.txt data2.txt
NR 的值依次爲:1,2……40,41,42……90
FNR的值依次爲:1,2……40, 1, 2……50
8.重定向輸出和特別函數getline實例
a.awk ‘{print FILENAME,$0}’ data1.txt data2.txt >data_all.txt
把第一個文件和第二個文件合併到data_all.txt中,新文件第一列爲原始文件名,後面列爲原始文件內容。
b.awk ‘$1!=fd{close(fd);fd=$1} {print substr($0,index($0,“ ”)+1)>$1}’ data_all.txt
把合併後的新文件data.txt重新拆開成原來的兩個子文件,依照新文件的第一列來產生新文件名。生成一個完整子文件後,關閉之;再生成下一個子文件。
A.getline從整體上來說,應這麼理解它的用法:
當其左右無重定向符 | 或 < 時,getline作用於當前文件,讀入當前文件的第一行給其後跟的變量 var 或$0(無變量);應該注意到,由於awk在處理getline之前已經讀入了一行,所以getline得到的返回結果是隔行的。
當其左右有重定向符 | 或 < 時,getline則作用於定向輸入文件,由於該文件是剛打開,並沒有被awk讀入一行,只是getline讀入,那麼getline返回的是該文件的第一行,而不是隔行。
B.getline用法大致可分爲三大類(每大類又分兩小類),即總共有6種用法。代碼如下:
nawk ‘BEGIN{“cat data.txt”|getline d; print d}’ data2.txt
nawk ‘BEGIN{“cat data.txt”|getline; print $0}’ data2.txt
nawk ‘BEGIN{getline d < “data.txt”; print d}’ data2.txt
nawk ‘BEGIN{getline < “data.txt”; print $0}’ data2.txt
以上四行代碼均實現“只打印data.txt文件的第一行”(若打印全部行,用循環)
eg. nawk ‘BEGIN{FS=”:”;while(getline<”/etc/passwd”>0){print $1}}’ data.txt
nawk ‘{getline d; print d”#”$3}’ data.txt
awk首先讀入第一行,接着處理getline函數,然後把下一行指定給變量d,再先打印d,由於d後面有換行符,所以後面緊跟的#會覆蓋d,後面的$3同樣也會覆蓋d。
nawk ‘{getline; print $0”#”$3}’ data.txt
awk首先讀入第一行接着處理getline函數,然後把下一行指定給$0,現在的$0已經是下一行內容,後面的#和$3(從$0中取)會覆蓋$0的內容。
c.nawk ‘BEGIN{system(“echo \”input your name:\” ”);getline var; print “\nYour name is”,d,”\n”}’
系統提示輸入名字,然後將輸入的名字打印出來。特別注意:該awk語句後沒有輸入文件名,而是利用鍵盤輸入作爲文件名。
4
經 典 實 例
1.問題描述
有如下銀行帳單部分文本,
200000000000007|shi yan city qi pei bu
202111320000018|hospital
200000000000007|shiyan city renmen road qi bei bu
201602520002941|middle school
200000000000007|mingfeng road qi pei
201602120000113|zhuanghuang factory
201602320000115|liangyou factory
要求:將第一列中重複的合併爲一行,其第二列填入最長地址的那列
得到的結果應爲:
200000000000007|shiyan city renmen road qi bei bu
202111320000018|hospital
201602520002941|middle school
201602120000113|zhuanghuang factory
201602320000115|liangyou factory
代碼:(假設下面代碼在文件myawk..sh中)
#!/usr/bin/nawk -f
BEGIN{FS=OFS="|"; i=1;}
{ if(a[$1]==0){b[i]=$1;a[$1]=$2;i++}
if(length(a[$1])<length($2)){a[$1]=$2}
}
END{for(j=1;j<i;j++) print b[j],a[b[j]]
}
執行:myawk.sh data.txt
比較代碼:(實現不全)
awk 'BEGIN{FS=OFS="|"} !(length(a[$1])>length($2)){a[$1]=$2} END{for(i in a)print i,a[i]}' data.txt
解釋:此例中,用了兩個數組,a用來與$1關聯,b用來順序記錄,使得在最後打印時是完全按照$1順序打印。條件句首先判斷數組元素是否是第一次寫入,若非,則比較當前$2值和以前儲存的值長度。
功能不全代碼不能順序打印。當$1有重複,而$2長度第一次、第二次、第三次是以遞減的方式時,該代碼應用得較好。但是,當$2各次得到的長度不確定時,代碼不能實現上述功能。例如:本例中第5行的第二列若比第3行的第二列長度長時,功能不全代碼就不能實現要求。 5
2.問題描述:
有兩個文件如下:
data1文件內容:
1 0.5 100
10 15 36.5
data2文件內容:
50 10 19
3.2 1 5
要求:得到一個新的文件,內容是data1和data2對應的列數字相加的和,新文件內容:
51 10.5 119
13.2 16 41.5
代碼:(假設下面代碼在文件myawk..sh中)
#!/usr/bin/nawk -f
{ for(i=1;i<=NF;i++) a[i]=$i;
getline < “data2.txt”
for(j=1;j<NF;j++) printf $j+a[j] “\t”
printf $NF+a[NF]“\n”
}
執行:myawk.sh data1.txt
解 釋: 上述代碼用到了getline同時讀入第二個文件的行內容並存入到$0中,最後用printf來實現相加後的打印。注意後面同時緊跟打印了\t和\n,以 用來列之間的分隔和行之間的分隔。此外,也可以用print來實現,但是print每執行完一次都要換行,(當然可以用OFS來進一步處理)。
實際上也可以用另外一種辦法來實現,就是用NR、FNR比較,然後分別將兩個文件賦值給不同的數組,然後相加。當然代碼就沒有上述代碼簡練。
3.問題描述
有兩個如下文件:
data2.txt 文件內容如下:
01 1111 AAA WW001 $$$$ 1000.00
02 2222 BBB GG001 %%%% 2000.00
03 3333 CCC JJ001 **** 3000.00
04 4444 DDD FF002 &&&& 4000.00
05 5555 EEE RR002 @@@@ 5000.00
06 666 FFF UU003 JJJJ 6000.00
07 777 III II005 PPPP 7000.00
08 8888 TTT TT008 TTTT 8000.00
data1.txt 文件內容如下:
AAA 001 1000.00
BBB 001 2000.00
DDD 002 4000.00
EEE 002 5000.00
FFF 003 6000.00
data1的第1列、最後一列和data2的第3列、最後一列是相同的,
data1的第2列和data2的第4列的後3位是相同的。
要求:data2中第三列、第四列後3位、第六列完全和data1相同的取出來得到新文件如下內容: 6
01 1111 AAA WW001 $$$$ 1000.00
02 2222 BBB GG001 %%%% 2000.00
04 4444 DDD FF002 &&&& 4000.00
05 5555 EEE RR002 @@@@ 5000.00
06 666 FFF UU003 JJJJ 6000.00
代碼:
nawk 'NR<=FNR {a[$1]=$1"x"$2"x"$3}
NR>FNR { b=substr($4,3);
c=$3"x"b"x"$6;
if(c==a[$3]) print}' data1.txt data2.txt
執行:直接執行上述代碼
解釋:本例巧妙地利用了組合字符串(用x來組合),然後用來比較。
當NR<=FNR時,處理第一個文件,當NR>FNR時,處理第二個文件。
-----------------------------------------------------------------------------------------------------------------
後記:
花了約有一個星期的時間來整理這些有關awk的材料。希望這些小小的總結實例能給初學者帶來拋磚引玉的作用。上述總結期望能概括awk的大部分內容,足以應付日常的維護工作。其他,例如自定義函數等未能包括在其中。
本總結的主要目的是以代碼帶動理解,每行代碼均在Solaris下運行通過。當然某些地方有打字上的疏漏,請指正。至於基礎文字上的說明請參閱其他網頁。
上述語法實例材料均取自於chinaunix論壇,本人只是文字整理和重新排版。在此特別感謝那些付出努力的網友們。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章