1. 程序元素
一個awk 程序是一對以模式(pattern) 與大括號框起來的操作(action) 組合而成的,或許,還會加上實現操作細節的函數(function ) 。針對每個匹配于輸人數據的模式,操作會被執行,且所有模式都會針對每條輸人記錄而檢查。模式或操作可省略其中一個。如果模式省略,則操作將被應用到每條輸人記錄; 如果操作省略,則默認操作為打印匹配之記錄在標準輸出上。以下是傳統awk 程序的配置:
pattern {action} 如模式匹配,則執行操作
pattern {action} 如模式匹配,則打印記錄
雖然,模式多半是數字或字符串表達式,不過awk 以保留字BEGIN 與END 提供兩種特殊模式。
與BEG 工N 關聯的操作只會執行一次,在任何命令行文件或一般命令行賦值被處理之前,但是在任何開頭的一V 選項指定已經完成之后。
END 操作也是只執行一次,用于所有輸入數據已被處理完之后。它多半用于產生摘要報告,或是執行清除操作。
BEGIN 與END 模式可以是任意順序,可以存在于awk 程序內的任何位置。不過,為了方便,我們通常將BEGIN 模式放在程序的第一個位置,而將END 模式放在最后。
2. 注釋與空白
awk 里的注釋是從# 開始到該行結束,就像在Shell 里那樣。空行等同于空的注釋。
3. 字符串與字符串表達式
awk 字符串包含零至多個字符,且在字符串的長度上沒有限制,視可用內存而定。
字符串的比較,用的是傳統的關系運算符:==( 相等) 、!=( 不等) 、<( 小于) 、<=( 小于等于) 、>( 大于) ,以及>=( 大于等于》。比較后返回l 為真,0 為假。比較不同長度的字符串,且其中一個字符串為另一個的初始子字符串時,較短的會定義為小于較長的那個,因此,“A ”< “AA ”的值為真。
awk 并無特殊的字符串接續運算符。也就是說,兩個連續字符串,會自動地連接在一起。以下每一組賦值設置標量變量。為相同的具有四個字符的字符串:
s = “ABCD ”
s = “AB ”“CD ”
s = “A ”“B ”“CD ”
s = “A ”“B ”“C ”“D ”
字符串不需要是常數,如果我們繼續上述的賦值:
t= s s s
則t 的值為“ABCDABCDABCD “。.
將數字轉換為字符串,通過數字連接空字符串即可
n =123 ,
接著是:
s = ““ n ,把值“123 ”賦給s 。
awk 功能強大的地方大多來自于它對正則表達式的支持。有兩個運算符:~( 匹配) 與!~( 不匹配) 讓awk 更容易使用正則表達式:”ABC ”~ ”^[A-Z]+$ “,結果為真。
4. 數值與數值表達式
所有awk 里的數字,都以雙精確度的浮點值表示。浮點數可以包含一個末端以字母e( 或E) 所表示的10 次方指數以及可選地帶正負號的一個整數。舉例來說:0.03125, 3.125e-2, 3125e-5 與0.003125E1 ,同樣都是表示1/32 。因為awk 里所有算術都是浮點算術。
awk 并沒有提供字符串轉數字的函數,不過awk 的做法很簡單:只要加個零到字符串里,例如:s="123" ,接著是n=0+s ,便將數字123 賦值給n 了。
5. awk 的數值運算符
表9 一:awk 的數值運算符( 優先級由大到小排列)
運算符 說明
++ -- 增加與減少( 前置或后置)
^ ** 指數( 右結合性)
! + - 非、一元(unary) 加號、一元減號
* / % 乘、除、余數
+ - 加、減
< <= == != > >= 比較
&& 邏輯AND( 簡寫)
|| 邏輯OR( 簡寫)
?: 三元條件式
= += -= *= /= %= ^= **= 賦值( 右結合性)
6. 標量變量
保存單一值的變量叫做標量變量。
a wk 的變量名稱必須以ACSII 字母或下劃線開始,然后選擇性地接上字母、下劃線及字。因此,變量名稱要匹配正則表達式[A-Za-z-][A-Za-z_0-9]* 。變量名稱在實際上并沒有長度的限制。awk 的變量名稱是與大小寫有關的:foo, Fo 。與FOO 是完全不同的三個名稱。一般使用上以及建議用法是: 養成習慣,將局部變量全設為小寫、全局變量第一個字母為大寫,而內建變量則全是大寫。
7. 數組變量
awk 允許在數組名稱之后,以方括號將任意數字或字符串表達式 括起來作為索引。例如:
telephone["Alice"]= “555-0134"
telephone["Bob"]= “555-0135"
telephone["Carol”]= “555-0136"
telephone["Don"]= “555-0141"
以任意值為索引的數組,稱之為關聯數組,因為它們的名稱與值是相關聯的。重要的是,awk 將其應用于數組中,允許查找( find ) 、插入(insert ) 以及刪除( remove) 等操作,在一定的時間內完成,與存儲多少項目無關。
一個變量不能同時用作標量變量和數組變量。當你應用delet 。語句刪除數組的元素
(element] 的時候,不會刪除它的名稱。因此。像這樣的代碼:
x[1]=3
delete x
x=789
會引發awk 發出提示,告訴你不可以給數組名稱賦值.
8. 命令行參數
awk 通過內建變量ARGC( 參數計數) 與ARGV( 參數向量,或參數值) ,讓命令行參數
可用。下面簡短的程序說明其用法;
[root@local~]#cat showargs.awk
BEGIN{
print ”ARGC= ”,ARGC
for (k=0;k<ARGC; k++)
print "ARGV[”k”]=[”ARGV[k] “
}
再來看看將它用在一般awk 命令行上,會產生什么樣的結果:
[root@local~]# awk -v One=1 -v Two=2 -f showargs.awk Three=3 file1 Four=4 filet2 file3
ARGC=6
ARGV[0]=[awk]
ARGV[1]=[Three=3]
ARGV[2]=[file1]
ARGV[3]=[Four=4]
ARGV[4]=[file2]
ARGV[5]=[file3]
9. 環境變量
awk 提供訪問內建數組ENV 工RON 中所有的環境變量:
[root@local~]#awk 'BEGIN {print ENVIRON["HOME"];print ENVIRON["USER]} ‘
/home/Jones
hones
通常你應將ENVIRON 看成是一個只讀數組。
10. 模式
模式由字符串與 / 或數值表達式構建而成。常用的模式如下:
NF==0 選定空記錄
NF>3 選定擁有三個字段以上的記錄
NR<5 選定第 1 到第 4 條記錄
(FNR==3)&&(FILENAME~/[.] [ch]$/) 來源于 C 源文件中選定記錄 3
$1~/Jones/ 選定字段 1 里有 . ” jones “的記錄
/[Xx][Mm][Ll]/ 選定含有‘ 'XML' 。的記錄,并忽略大小寫差異
$0~/[Xx][Mm][Ll]/ 同上
11. 操作
以最簡單的形式來說,純print 意指在標準輸出上,打印當前的輸入記錄($0) ,接著是輸出記錄分隔字符)ORS 的值,默認為單一換行字符。因此,下面這些程序所做的全是相同的操作:
1 模式為真,默認操作為打印
NR>0 {print} 有記錄時打印( 恒為真)
1 {print} 模式為真。則打印,這是默認值
{print} 無模式則視為真,明確的打印,這是默認值
{print $0} 相同,但打印明確的值
下面的例子已經是完整的awk 程序。在每一個中,我們都只顯示前三個輸入字段,并通過省略選定模式,選定所有的記錄。awk 程序語句以分號分隔,而且我們會使用些略微不同的操作代碼,以修改輸出字段分隔字符:
[root@local~]#echo ‘one two three four'| awk ‘{print $1,$2,$3}’
one two three
[root@local~]#echo ‘one two three four'| awk ‘{OFS=”…”;print $1,$2,$3}’
one…two…three
[root@local~]#echo ‘one two three four'| awk ‘{OFS=”\n”;print $1,$2,$3}’
one
two
three
改變輸出字段分隔字符而沒有指定任何字段,不會改變$0:
[root@local~]#echo ‘one two three four'| awk ‘{OFS=”\n”;print $0}’
one two three four
不過,如果我們更改輸出字段分隔字符,并指定至少一個字段( 即使我們未變更其值) ,
強制以新的字段分隔字符重新組合記錄,則結果為:
[root@local~]#echo ‘one two three four'| awk ‘{OFS=”\n”;$1=$1;print $0}’
one
two
three
four
12. 在awk 中的單行程序
1.UNIX 單詞計數程序wc;
[root@local~]#awk ‘{C+=length($0)+1;w+=NF} END {print NR, W, C}’
2. 撇開NUL 字符問題,awk 其實可以輕松取代cat ,下面這兩個例子會產生相同輸出:
[root@local~]# cat*.xml
[root@local~]# awk 1*.xml
3. 要將原始數據值及它們的對數打印為單欄的數據文件,可使用:
[root@local~]# awk ‘{print $1, log($1)}’file(s)
4. 在以空白分隔字段的表格中,報告第n 欄的和:
[root@local~]# awk -v COLUMN=n ‘{sum+=$COLUMN} END {print sum} ’file (s)
5. 微調上述報告,產生字段n 的平均值:
[root@local~]# awk -v COLUMN=n ‘{sum+=$COLUMN} END {print sum/NR } ’file (s)
6. 針對花費文件( 其記錄包含描述與金額于最后一個字段) ,打印花費總數??墒褂脙冉ㄗ兞縉F 計算總值:
[root@local~]# awk’{sum+=$NF; print $0, sum}’files)
7. 這里是三種查找文件內文本的方式:
[root@local~]#egrep ‘pattern|pattern’ file (s)
[root@local~]#awk ‘/pattern|pattern/’file (s)
[root@local~]#awk ‘/pattern}pattern/ {print FILENAME ”: ”FNR ”: ”$0} ’file(s)
8. 如果你要限制僅查找100 一150 行,可以通過兩個工具程序,再搭配管道,不過這么做會漏掉位置信息:
[root@local~]#sed -n -e 100,150p -s file(s) | egrep 'pattern'
使用GNU sed 要搭配-s 選項,才能為每個文件重新開始行編號。另外,你也可以通過awk ,使用比較花哨的模式來做:
[root@local~]#awk ‘(100<=FNR) && (FNR <= 150) && /pattern/\
{print FILENAME ”:” FNR ”:”$0}’file(s)
9. 要在一個四欄表格里,調換第二與第三欄,假設它們是以制表字符分隔,那么可以
使用下面三種方式的其中一種:
[root@local~]#awk -F'\t’-v OFS='\t’{print $1, $3, $2, $4}’old > new
[root@local~]#awk ‘BEGIN{FS=OFS="\t"}{print $1, $3 ,$2 ,$4} ’old>new
[root@local~]#awk –F ‘\t’{print $1"\t" $3"\t" $2"\t" $4}’old>new
10. 要將各欄分隔字符由制表字符( 在此以·顯示) 轉換成& ,可在以下兩種方式擇一:
[root@local~]#sed -e 's/ ·/&/g' file(s)
[root@local~]#awk ‘{BEGIN{FS="\t";OFS= “& ”}{$1=$1; print} ’file(s)
11. 下面這兩個管道,都為刪除已排序流里的重復行
[root@local~]#sort file(s)|uniq
[root@local~]# sort file(s)|awk ‘Last!=$0 { print }{Last=$0}’
12. 將回車字符/ 換行字符的行終結,一致轉換為以換行字符作為行終結,可在下列方
式中選擇一種:
[root@local~]#sed –e ‘s/\r$//’ file(s)
[root@local~]#sed –e ‘s/^M$//’ file(s)
[root@local~]# mawk ‘BEGIN {RS=“\r\n"} {print}’file(s)
13. 要將單空格的文本行,轉換為雙空格的行,可在下列方式選擇一種
[root@local~]#sed –e ‘/s/$/\n/ ’file(s)
[root@local~]#awk ‘BEGTN{ ORS = "\n\n"){print}’file(s)
[root@local~]#awk ‘BEGIN{ ORS="\n\n" }1 ’ file(s)
[root@local~]#awk {print $0 “\n”} ’ file(s)
[root@local~]#awk ‘{print;print ” ”} ’ file(s)
13. 語句
13.1. 條件語句
if(expressionl)
stateme 刀t1
else if(expression2)
statement2
else if(expression3)
statement3
else if(expressionk)
statementk
else
statementk+l
13.2. 重復執行
awk 提供了 4 種重復執行語句 ( 循環 ):
1. 循環在起始處使用結束測試 :
while(expression)
statement
2. 循環在結尾處使用結束測試 :
do
statement
while (expression)
3. 循環執行可計數的次數 :
for(expr1;expr2; expr3)
statement
4. 循環處理關聯數組里的元素 :
for(key in array)
statement
例如:
for (name in telephone)
print name“\t" telephone[name]
13.3 數組成員測試
成員測試key in array 是一個表達式: 如果key 為array 的一個索引元素,則計算為1( 真) 。如果key 不是array 的一個索引元素,則!(key in array) 為1 。
對于具有多下標(subscript) 的數組,在測試時,請使用圓括號,并以逗點分隔下標列表:(i ,j ,…,n)in array
成員測試不可能建立數組元素,然而引用元素時,如果元素不存在,便會建立它。因此你應該這么寫:
if("Sally" in telephone)
print "Sally is in the directory"
而非:
if (telephone["Sally"]!= ””)
print "Sally is in the directory"
因為第二種形式會在她(Sally) 不存在時,將其加入到目錄里,并擁有一個空電話號碼。
重點是: 你必須能夠區分尋找索引(index) 與尋找特定值(value) 的差異。索引成員測試需要固定的時間,而值的查找時間是與數組里元素的個數成正比,這點我們在先前已通過break 語句內的for 循環解釋過了。如果你需要時常用到這兩種運算,那么構建反索引數組會比較實用:
for (name in telephone)
name_by_telephone[telephone[name]]=name
接下來,你就可以使用name_by_telephone ["555-0136"] 在一定時間內找到”Carol" 。當然,這里假定所有的值是唯一的: 如果這兩人共享同一個電話,則name_by_telephone 數組只會記錄最后一個名稱。只要稍做修改就能解決這個問題:
for (name in telephone)
{
if (telephone[name] in name_by_telephone)
name_by_telephone[telephone[name]]=\
name_by_telephone [telephone[name]) “\t”name
e1se
name_by_telephone[telephone[name]]=name
現在,name_by_telephone 即包含了以制表字符分隔的具有相同電話號碼的人名列表。
14. 用戶控制輸入
awk 也可以通過的getline 語句做這件事。getline 會返回一個值,當輸入被成功讀取時,它的返回值為++I ,而返回值為0 時,則表示在文件結尾,而-1 則表示錯誤。它的用法很多,見表。
語法 說明
getline 從當前輸入文件中,讀取下一條記錄,存入$0 ,并更新NF, NR 與FNR
getline var 從當前輸入文件中,讀取下一條記錄,存入var ,并更新NR 與FNR
getline<file 從file 文件中,讀取下一條記錄,存入$0 ,并更新NF, NR 與FNR
getline var<file 從file 文件中,讀取下一條記錄,存入var ,并更新NF, NR 與FNR
cmd|getline 從外部命令cmd 讀取下一條記錄,存入$0 ,并更新NF
cmd|getline var 從外部命令cmd 讀取下一條記錄,存入var
命令管道在awk 里可以發揮強大的功能。管道可以在字符字符串中標明,也可以包含任
意的Shell 命令。這里是與getline 搭配使用,如下:
"date" I getline now
close("date")
print "The current time is".now
接下來說明的是: 如何在循環里使用命令管道:
command="head -n 15 /etc/hosts"
while((command I getline s)>0)
print s
close(command)
15. 執行外部程序
這里是解決電話名錄排序問題較短的程序方案,使用臨時性文件與systemty ,而非awk 管道:
tmpfile= “/tmp/telephone.tmp^
for (name in telephone>
print name "\t" telephone[name]>tmpfile
close(tmpfilej
system("sort < tmpfile “)
臨時性文件必須在調用system() 之前關閉,以確保任何緩沖區輸出都正確地記錄在文件內。
對于被system() 執行的命令并不需要調用close() ,因為close() 僅針對以I/O 重定向運算符所打開的文件或管道,還有getline, print 或printf 。
傳遞給system[f 的命令可包含數行
system("cat <<EOFILE\nuno\ndos\ntres\nEOFILE"
它產生的輸出和從嵌入文件復制到標準輸出一樣
Un0
das
tres
16. 用戶自定義函數
函數定義如下:
function name(argl, }rg2, …,argn
{
statements
}
指定的參數在函數體中用來當作局部變量,它們會隱藏任何相同名稱的全局性變量。函數也可用于程序它處,調用的形式為:
name(exprl, expr2, …,expn) 忽略任何的返回值
result=name(exprl, expr2, …,exprn) 將返回值存儲到result 中
在每個調用點上的表達式,都提供初始值給函數參數型變量。以圓括號框起來的參數,必須緊接于函數名稱之后,中間沒有任何空白。
對標量參數所做的變動,調用者無從得知,不過對數組的變動就可看見了。換句話說,標量為傳值(by vaule ) ,而數組則為傳引用(by reference): 這對C 語言也是這樣。
函數體里的return expression 語句會終止主體的執行,并將expression 的值與控制權傳給調用點。如果expression 省略,則返回值由實現期定義。我們測試過的所有系統,返回的不是數字零就是空字符串。
17. 字符串函數
17.1. 子字符串提取
提取子字符串的函數:substr(string, start, 1en) ,會返回一份由string 的start 字符開始,共len 個字符長度的子字符串副本。字符的位置,從1 開始編號:substr("abcde", 2, 3) 將返回。bcd" 。 len 參數可省略,省略時,則默認為length(string)-start+1 ,選出字符串的剩余部分。
17.2. 字符串大小寫轉換
tolower(string) 會返回將所有字母改為同義的小寫的string 副本,而toupper(string) 則返回被改為大寫字母的string 副本。所以tolower("aBcDeF123") 返回”abcdef123",toupper("aBcDeF123") 返回"ABCDEF123" 。
17.3. 字符串大小寫轉換
index(string, find) 查找string 里是否有字符串find ,然后返回string 里find 字符串的起始位置,如果在string 里找不到find ,則返回0 。例如index("abcdef","de") 會返回4 。
17.4. 字符串匹配
match (string, regexp) 將string 與正則表達式regexp 匹配,如果匹配,則返回
匹配string 的索引,不匹配,則返回0 。這種方式提供了比表達式(string~regexp) 還多的信息,后者只能得到計算值1 或0 。另外match ( ) 也具有一個有用的副作用: 它會將全局變量RSTART 設為在string 中要開始匹配的索引值,而將RLENGTH 設為要匹配的長度。而匹配子字符串則以substr(string, RSTART, RLENGTH) 表示。
17.5. 字符串替換
awk 在字符串替換功能上,提供兩個函數:sub(regexp, replacement, target) 與gsub(regexp, replacement, target), sub() 將target 與正則表達式regexp 進行匹配,將最左邊最長的匹配部分替換為字符串replacement 。gsub() 的運行則有點類似,不過它會替換所有匹配的字符串( 前置g 表示global 全局之意) 。
17.6. 字符串替換
awk 針對當前輸人記錄$0 自動提供了方便的分割為字1, $}, …、$NF ,也可以函數來做:split(string, array, regexp) 將string 切割為片段,并存儲到array 里的連續元素。在數組里,片段放置在匹配正則表達式regexp 的子字符串之間。如果regexp 省略,則使用內建字段分隔字符FS 的當前默認值。函數會返回array 里的元素數量。
17.7. 字符串重建
join() 可確保參數數組不會被引用到,除非索引是在范圍之內。否則,一個具有數組長度為0 的調用可能會建立arrayfl3 ,而修改了調用者的數組。插人的字段分隔字符為普通字符串,而非正則表達式,所以針對傳遞給split() 的一般正則表達式,join() 不會重建精確的原始字符串。
17.8. 字符串格式化
最后一個與字符串相關的函數是在用戶控制下格式化數字與字符串:sprintf (format,expression1, expression2, …) ,它會返回已格式化的字符串作為其函數值。printf() 的運行方式也是這樣,只不過它會在標準輸出或重定向的文件上顯示格式化后的字符串,而不是返回其函數值。較新的程序語言以更強大的格式化函數來取代格式控制字符串,但相對而言讓代碼變得很冗長。按照傳統的文本處理應用來說,sprintf 與printf
18. 數值函數
函數 說明
atan2(y, x) y 返回y/x 的反正切,值介于-pai 與+pai 之間。
cos(x) 返回x 的余弦值( 以弧度(radians) 計算) ,該值介于-1 與+1 之間
exp(x) 返回x 的指數,ex,
int(x) 返回x 的整數部分,截去前置的0
log(x) 返回x 的自然對數。
rand() 返回平均分布的虛擬隨機r,O<=r<l
sin(x) 返回x 的正弦值( 以弧度(radians] 計算) ,該值介于-1 與+1 之間
sqrt(x) 返回x 的平方
srand(x) 設置虛擬隨機產生器的種子為x ,并返回正確的種子。如果省略x ,則使用當前時間( 以秒計) 。如果。rand ( ) 未被調用,則awk 在每次執行時會從相同的默認種子開始;mawk 則不會。
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/wzhwho/archive/2010/04/21/5513791.aspx