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 }列印每個記錄,也就是把輸入完好的複製到輸出。更有用的是列印來自每個記錄的一個欄位或某些欄位。例如
awk ‘{print $2, $1}’ filename
按逆序列印前兩個欄位。在 print 語句中用逗號分隔的項,在輸出的時候會用當前輸出欄位分隔符分隔開。沒有用逗號分隔的項會串聯起來,所以
CODEawk ‘{ print $1 $2 }’ filename
把第一個和第二個欄位合在一起。
可以使用預定義的變數 NF 和 NR;例如
CODEawk ‘{ print NR, NF, $0 }’ filename
打印出前導了記錄數和欄位數的每個記錄。
輸出可以被重定向到多個檔案中
CODEawk ‘{ print $1 >"foo1"; print $2 >"foo2" }’
寫第一個欄位 $1 到檔案 foo1 中,寫第二個欄位到檔案 foo2 中。還可以使用 >> 符號:
CODEawk ‘{ print $1 >>"foo" }’ filename
新增輸出到檔案 foo。(在每種情況下,輸出檔案都在必要時建立)。
檔名可以是一個變數或欄位,同常量一樣;例如
CODEawk ‘{ print $1 >$2 }’ filename
使用欄位 2 的內容作為檔名字。
自然的,有對輸出檔案數目的限制,目前是 10 個。
awk 還提供 printf 語句用於輸出格式化:
printf format,expr, expr, …
依據在 format 中的規定格式化在列表中的表示式並列印它們。例如,
awk ‘{ printf "%8.2f %10ld\n", $1, $2 }’ filename
列印 $1 為 8 位寬的小數點後有兩位的浮點數,列印 $2 為 10 位長的長十進位制數,並跟隨著一個換行。不自動生成輸出分隔符;你必須自己增加它們,如這個例子那樣。這個版本的printf 同於C 所使用的。
輸出
1.抽取域
awk -F: ‘{print $1}’ /etc/passwd # -F 指定欄位分割符
2.儲存輸出
CODEawk -F: ‘{print $1}’ /etc/passwd | tee user
awk -F: ‘{print $1}’ /etc/passwd >user
3.使用標準輸出
CODEawk ‘/root/’ /etc/passwd #
/xxx/為正則表示式,表示列印包含"root"的行
4.列印所有記錄
CODEawk ‘{print $0}’ /etc/passwd
5.打印表頭
CODEawk -F: ‘BEGIN {print "NAME\n"} {print $1}’ /etc/passwd
6.打印表尾
CODEawk -F: ‘{print $1} END {print "this is all users"}’ /etc/passwd
條件操作符
1.匹配
awk ‘{if($1~/root/) print $0}’ /etc/passwd #如果field1包含"root",列印該行
2.精確匹配
!= ==
3.不匹配
!~
4.大小比較
> >= < <=
5.設定大小寫
CODEawk ‘/^[Rr]oot/’ /etc/passwd # 列印包含行首為Root或者root的行
6.任意字元
CODEawk ‘$2~/^…a/’ /etc/passwd #
列印第二個欄位開頭第四個字母為a的行
7.或關係匹配
CODEawk ‘/(root|ftp)/’ /etc/passwd #列印包含"root"或者"ftp"的行
8.AND && OR ||
CODEawk ‘{$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。設定RSTART和RLENGTH的值 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):
CODEecho ababab | awk ‘gsub(/a/,"c")’ # cbcbcb
sub(r,s,t)
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 CODEpai=$(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模組後的左括號要在同一行。