1. 程式人生 > >AWK學習總結及練習

AWK學習總結及練習

概述awk 是一種程式語言,她是由AT&T 貝爾實驗室的Alfred Aho, Peter Weinberger 和Brian Kernighan開發的,Brian Kernighan。目前仍在維護及增強awk。awk的語法與C類似。

呼叫
1.awk ‘pattern-action statements’ input_file_list
2.將awk命令插入一個檔案,並使awk程式可以執行,然後用awk命令直譯器作為指令碼首行。
3.將所有awk命令插入一個單獨檔案
awk -f awk-script-file inputfile

模式和動作
一個awk 程式是由一系列的"模式-動作"語句構成的:
pattern {action}
pattern {action}
pattern {action}
……

awk 程式為每個輸入行依次地進行每一個"模式"的匹配尋找,對每一個匹配上的模式執行相應的"動作",接著讀取下一行並再次開始匹配,直到所有的輸入都處理完畢。
在一條語句中可以省略"模式"或者"動作",預設的模式為匹配所有行,預設的動作為輸出當前行:print $0。無論何時,動作都必須用花括號引起來。
awk 從輸入中一次讀取一行(一條記錄),預設的行分割符(記錄分割符)為\n。
然後awk 將記錄分割為一個個的欄位,預設的欄位分割符為“Blank”(空白)。一行中的第一個欄位稱為$1,第二個欄位稱為$2,. . . ,整個記錄稱為$0。

列印
一個動作可以沒有模式,在這種情況下動作在所有行上執行。最簡單的動作是列印某些或所有的記錄;這可以通過 awk 命令 print 來完成。awk 程式{ print }列印每個記錄,也就是把輸入完好的複製到輸出。更有用的是列印來自每個記錄的一個欄位或某些欄位。例如

CODE

awk ‘{print $2, $1}’ filename

按逆序列印前兩個欄位。在 print 語句中用逗號分隔的項,在輸出的時候會用當前輸出欄位分隔符分隔開。沒有用逗號分隔的項會串聯起來,所以

CODE

awk ‘{ print $1 $2 }’ filename

把第一個和第二個欄位合在一起。

可以使用預定義的變數 NF 和 NR;例如

CODE

awk ‘{ print NR, NF, $0 }’ filename

打印出前導了記錄數和欄位數的每個記錄。

輸出可以被重定向到多個檔案中

CODE

awk ‘{ print $1 >"foo1"; print $2 >"foo2" }’

 filename

寫第一個欄位 $1 到檔案 foo1 中,寫第二個欄位到檔案 foo2 中。還可以使用 >> 符號:

CODE

awk ‘{ print $1 >>"foo" }’ filename

新增輸出到檔案 foo。(在每種情況下,輸出檔案都在必要時建立)。

檔名可以是一個變數或欄位,同常量一樣;例如

CODE

awk ‘{ print $1 >$2 }’ filename

使用欄位 2 的內容作為檔名字。

自然的,有對輸出檔案數目的限制,目前是 10 個。

awk 還提供 printf 語句用於輸出格式化:
printf format,expr, expr, …
依據在 format 中的規定格式化在列表中的表示式並列印它們。例如,

CODE

awk ‘{ printf "%8.2f %10ld\n", $1, $2 }’ filename

列印 $1 為 8 位寬的小數點後有兩位的浮點數,列印 $2 為 10 位長的長十進位制數,並跟隨著一個換行。不自動生成輸出分隔符;你必須自己增加它們,如這個例子那樣。這個版本的printf 同於C 所使用的。

輸出
1.抽取域

CODE

awk -F: ‘{print $1}’ /etc/passwd # -F 指定欄位分割符

2.儲存輸出

CODE

awk -F: ‘{print $1}’ /etc/passwd | tee user

awk -F: ‘{print $1}’ /etc/passwd >user

3.使用標準輸出

CODE

awk ‘/root/’ /etc/passwd # /xxx/為正則表示式,表示列印包含"root"的行

4.列印所有記錄

CODE

awk ‘{print $0}’ /etc/passwd

5.打印表頭

CODE

awk -F: ‘BEGIN {print "NAME\n"} {print $1}’ /etc/passwd

6.打印表尾

CODE

awk -F: ‘{print $1} END {print "this is all users"}’ /etc/passwd

條件操作符
1.匹配

CODE

awk ‘{if($1~/root/) print $0}’ /etc/passwd #如果field1包含"root",列印該行

2.精確匹配
!= ==

3.不匹配
!~

4.大小比較
> >= < <=

5.設定大小寫

CODE

awk ‘/^[Rr]oot/’ /etc/passwd # 列印包含行首為Root或者root的行

6.任意字元

CODE

awk ‘$2~/^…a/’ /etc/passwd # 列印第二個欄位開頭第四個字母為a的行

7.或關係匹配

CODE

awk ‘/(root|ftp)/’ /etc/passwd #列印包含"root"或者"ftp"的行

8.AND && OR ||

CODE

awk ‘{$1~/mail/ && $7==/bin/bash}’ /etc/passwd


系統變數:

ARGV 命令列引數陣列
ENVIRON 環境變數陣列
FILENAME 當前輸入檔名
FNR 當前檔案中的記錄號
FS 欄位分隔符
IGNORECASE 忽略正則表示式和串的大小寫
NF 當前記錄中的欄位數
NR 至今讀取的記錄數
OFMT 數的輸出格式,預設為"%.6g"
OFS 輸出欄位分隔符
ORS 輸出記錄分隔符
RS 輸入記錄分隔符
RSTART 由match() 匹配的第一個字元的索引
RLENGTH 由match() 匹配的串的長度
SUBSEP 下標分隔符,預設為"�34"

內建字串函式

gsub(r,s,t) 在字串t中,用字串s替換和正則表示式r匹配的所有字串。返回替換的個數。如果沒有給出t,預設為$0
index(s,t) 返回s 中字串t 的位置,不出現時為0
length(s) 返回字串s 的長度,當沒有給出s時,返回$0的長度
match(s,r) 返回r 在s 中出現的位置,不出現時為0。設定RSTARTRLENGTH的值
split(s,a,r) 利用r 把s 分裂成陣列a,返回元素的個數。如果沒有給出r,則使用FS。陣列分割和欄位分割採用同樣的方式
sprintf(fmt,expr_list) 根據格式串fmt,返回經過格式編排的expr_list
sub(r,s,t) 在字串t中用s替換正則表示式t的首次匹配。如果成功則返回1,否則返回0。如果沒有給出t,預設為$0
substr(s,p,n) 返回字串s中從位置p開始最大長度為n的字串。如果沒有給出n,返回從p開始剩餘的字串
tolower(s) 將串s 中的大寫字母改為小寫,返回新串
toupper(s) 將串s 中的小寫字母改為大寫,返回新串

gsub(r,s,t):

CODE

echo ababab | awk ‘gsub(/a/,"c")’ # cbcbcb


sub(r,s,t)

CODE

echo ababab | awk ’sub(/a/,"c")’ # cbabab

其餘函式自行嘗試。

內建算術函式

cos(x) 返回x的餘弦值
sin(x) 返回x的正弦值
int(x) 返回x的整數部分
log(x) 返回x的自然對數
sqrt(x) 返回x的平方根
antan2(x) 返回y/x的反正切,值在 -π到 π之間
rand() 返回隨機數r,0 <= r < 1
srand(x) 建立rand()的隨機種子,如果沒有指定種子,則按當天時間。返回舊的種子

cos(x):

CODE CODE

pai=$(echo "scale=66; a(1)*4" | bc -l)
awk -va=$pai ‘BEGIN{print cos(a/4)}’ OR awk ‘BEGIN{print cos(’$pai‘/4)}’#0.707107

其餘函式自行嘗試。

附算術運算子

x^y x的y次冪 
x**y 同上 
x%y 計算x/y的餘數(求模) 
x+y x加y 
x-y x減y 
x*y x乘y 
x/y x除y 
-y 負y(y的開關符號);也稱一目減 
++y y加1後使用y(前置加) 
y++ 使用y值後加1(字尾加) 
–y y減1後使用y(前置減) 
y– 使用後y減1(字尾減) 
x=y 將y的值賦給x 
x+=y 將x+y的值賦給x 
x-=y 將x-y的值賦給x 
x*=y 將x*y的值賦給x 
x/=y 將x/y的值賦給x x%=y 將x%y的值賦給x 
x^=y 將x^y的值賦給x 
x**=y 將x**y的值賦給x

awk的兩道練習題:

1. 將兩個檔案合併
原始檔:

for i in $(ls comb*); do echo "$i"; cat $i; done
comb1
--daihao --zhanghao
101      aa
102      bb
103      cc
comb2
--daihao --zhanghao 
101    aaaa
103    bbbb
104    dddd
awk 'NR==FNR {a[$1]=$2} NR>FNR {printf("%s\t%s\t%s\n",$1,a[$1],$2); delete a[$1]} END {for(i in a) printf("%s\t%s\t%s\n",i,a[i],"")}' comb*

2. 文字統計
原始檔:

cat case 
Mike Harrington:[510] 548-1916:250:100:175
Christian Dobbins:[408] 538-2358:155:90:201
Susan Dalsass:[206] 654-6279:250:60:50
Archie McNichol:[206] 548-1348:250:100:175
Jody Savage:[206] 548-1278:15:188:150
Guy Quigley:[916] 343-6410:250:100:175
Dan Savage:[406] 298-7744:450:300:275
Nancy McNeil:[206] 548-1278:250:80:75
John Goldenrod:[916] 348-4278:250:100:175
Chet Main:[510] 548-5258:50:95:135
Tom Savage:[408] 926-3456:250:168:200
Elizabeth Stachelin:[916] 440-1763:175:75:300
要求輸出成:
			***CAMPAIGN 1998 CONTRIBUTIONS***
-----------------------------------------------------------------------------
NAME			PHONE		Jan	Feb	Mar	Total Donated
-----------------------------------------------------------------------------
Mike Harrington  	[510] 548-1916	250	100	175	525 
Christian Dobbins     	[408] 538-2358	155	90 	201	446 
Susan Dalsass     	[206] 654-6279	250	60 	50 	360 
Archie McNichol    	[206] 548-1348	250	100	175	525 
Jody Savage      	[206] 548-1278	15 	188	150	353 
Guy Quigley     	[916] 343-6410	250	100	175	525 
Dan Savage      	[406] 298-7744	450	300	275	1025
Nancy McNeil      	[206] 548-1278	250	80 	75 	405 
John Goldenrod   	[916] 348-4278	250	100	175	525 
Chet Main        	[510] 548-5258	50 	95 	135	280 
Tom Savage      	[408] 926-3456	250	168	200	618 
Elizabeth Stachelin   	[916] 440-1763	175	75 	300	550 
-----------------------------------------------------------------------------
				SUMMARY
-----------------------------------------------------------------------------
The campaign received a total of $6137 for this quarter.
The average donation for the 12 contributors was 511.417.
The highest contribution was $1025.
The lowest contribution was $280.
使用的AWK指令碼如下:
#!/usr/bin/awk

BEGIN{
	FS = "[ :]";
	print "\t\t\t***CAMPAIGN 1998 CONTRIBUTIONS***";
	print "-----------------------------------------------------------------------------";
	print "NAME\t\t\tPHONE\t\tJan\tFeb\tMar\tTotal Donated";
	print "-----------------------------------------------------------------------------";
	sum = 0;
	highest = 0;
	lowest = 100000;
}
{
	$8 = $5+$6+$7
	sum += $8
	printf("%s %-12s\t%s %s\t%-3s\t%-3s\t%-3s\t%-4s\n", \
		   $1, $2, $3, $4, $5, $6, $7, $8)
	if(highest < $8)
		highest = $8
	if(lowest > $8)
		lowest = $8
}
END{
	print "-----------------------------------------------------------------------------";
	print "\t\t\t\tSUMMARY";
	print "-----------------------------------------------------------------------------";
	printf("The campaign received a total of $%s for this quarter.\n", sum);
	printf("The average donation for the %d contributors was %s.\n", NR, sum/NR);
	printf("The highest contribution was $%s.\n", highest);
	printf("The lowest contribution was $%s.\n", lowest);
}
注意BEGIN、END模組後的左括號要在同一行。