1. 程式人生 > >linux 之awk命令詳解

linux 之awk命令詳解

數學函數 mat loop 多次 finished 數組結構 save pre 新的

awk是一種程序語言,對文檔資料的處理具有很強的功能。awk名稱是由它三個最初設計者的姓氏的第一個字母而命名的: Alfred V. Aho、Peter J. We i n b e rg e r、Brian W. Kernighan。

awk 最初在1 9 7 7年完成。1985年發表了一個新版本的awk,它的功能比舊版本增強了不少。awk能夠用很短的程序對文檔裏的資料做修改、比較、提取、打印等處理。如 果使用C 或Pascal等語言編寫程序完成上述的任務會十分不方便而且很花費時間,所寫的程序也會很大。
awk不僅僅是一個編程語言,它還是Linux系統管理員和程序員的一個不可缺少的工具。
awk語言本身十分好學,易於掌握,並且特別的靈活。

gawk是G N U計劃下所做的awk,gawk最初在1 9 8 6年完成,之後不斷地被改進、更新。
gawk 包含awk 的所有功能。


1.gawk的主要功能


gawk 的主要功能是針對文件的每一行( l i n e ),也就是每一條記錄,搜尋指定的格式。當某一行符合指定的格式時,gawk 就會在此行執行被指定的動作。gawk 依此方式自動處理輸入文件的每一行直到輸入文件檔案結束。
gawk經常用在如下的幾個方面:
· 根據要求選擇文件的某幾行,幾列或部分字段以供顯示輸出。
· 分析文檔中的某一個字出現的頻率、位置等。
· 根據某一個文檔的信息準備格式化輸出。

· 以一個功能十分強大的方式過濾輸出文檔。
· 根據文檔中的數值進行計算。


2.如何執行gawk程序


基本上有兩種方法可以執行gawk程序。
如果gawk 程序很短,則可以將gawk 直接寫在命令行,如下所示:
gawk ‘program‘ input-file1 input-file2 ...
其中program 包括一些pattern和action。
如果gawk 程序較長,較為方便的做法是將gawk 程序存在一個文件中,
gawk 的格式如下所示:
gawk -f program-file input-file1 input-file2 ...
gawk 程序的文件不止一個時,執行gawk 的格式如下所示:

gawk -f program-file1 -f program-file2 ... input-file1 input-file2 ...


3.文件、記錄和字段


一般情況下,gawk可以處理文件中的數值數據,但也可以處理字符串信息。如果數據沒有存儲在文件中,可以通過管道命令和其他的重定向方法給gawk提供輸入。當然, gawk只能處理文本文件(A S C I I碼文件)。
電話號碼本就是一個gawk可以處理的文件的簡單例子。電話號碼本由很多條目組成,每一個條目都有同樣的格式:姓、名、地址、電話號碼。每一個條目都是按字母順序排列。
在gawk中,每一個這樣的條目叫做一個記錄。它是一個完整的數據的集合。例如,電話號碼本中的Smith John這個條目,包括他的地址和電話號碼,就是一條記錄。
記錄中的每一項叫做一個字段。在gawk中,字段是最基本的單位。多個記錄的集合組成了一個文件。
大多數情況下,字段之間由一個特殊的字符分開,像空格、TAB、分號等。這些字符叫做字段分隔符。請看下面這個/etc/passwd文件:
tparker;t36s62hs;501;101;TimParker;/home/tparker;/bin/bash
etreijs;2ys639dj3;502;101;EdTreijs;/home/etreijs;/bin/tcsh
ychow;1h27sj;503;101;Yvonne Chow;/home/ychow;/bin/bash
你可以看出/etc/passwd文件使用分號作為字段分隔符。/etc/passwd文件中的每一行都包括七個字段:用戶名;口令;用戶I D;工作組I D;註釋; h o m e目錄;啟始的外殼。如果你想要查找第六個字段,只需數過五個分號即可。
但考慮到以下電話號碼本的例子,你就會發現一些問題:
Smith John 13 Wilson St. 555-1283
Smith John 2736 Artside Dr Apt 123 555-2736
Smith John 125 Westmount Cr 555-1726
雖 然我們能夠分辨出每個記錄包括四個字段,但g a w k卻無能為力。電話號碼本使用空格作為分隔符,所以gawk認為Smith是第一個字段,John是第二個字段,13是第三個字段,依次類推。就gawk 而言,如果用空格作為字段分隔符的話,則第一個記錄有六個字段,而第二個記錄有八個字段。
所以,我們必須找出一個更好的字段分隔符。例如,像下面一樣使用斜杠作為字段分隔符:
Smith/John/13 Wilson St./555-1283
Smith/John/2736 Artside Dr/Apt/123/555-2736
Smith/John/125 Westmount Cr/555-1726
如果你沒有指定其他的字符作為字段分隔符,那麽gawk將缺省地使用空格或TAB作為字段分隔符。


4.模式和動作


在gawk語言中每一個命令都由兩部分組成:一個模式(pattern)和一個相應的動作
(action)。只要模式符合,gawk就會執行相應的動作。其中模式部分用兩個斜杠括起來,而動
作部分用一對花括號括起來。例如:
/pattern1/{action1}
/pattern2/{action2}
/pattern3/{action3}
所有的gawk程序都是由這樣的一對對的模式和動作組成的。其中模式或動作都能夠被省
略,但是兩個不能同時被省略。如果模式被省略,則對於作為輸入的文件裏面的每一行,動作
都會被執行。如果動作被省略,則缺省的動作被執行,既顯示出所有符合模式的輸入行而不做
任何的改動。
下面是一個簡單的例子,因為gawk 程序很短,所以將gawk 程序直接寫在外殼命令行:
gawk ‘/tparker/‘ /etc/passwd
此程序在上面提到的/etc/passwd 文件中尋找符合tparker模式的記錄並顯示(此例中沒有動作,所以缺省的動作被執行)。
讓我們再看一個例子:
gawk ‘/UNIX/{print $2}‘ file2.data
此命令將逐行查找file2.data文件中包含UNIX的記錄,並打印這些記錄的第二個字段。
你也可以在一個命令中使用多個模式和動作對,例如:
gawk ‘/scandal/{print $1} /rumor/{print $2}‘ gossip_file
此命令搜索文件gossip_file中包括scandal的記錄,並打印第一個字段。然後再從頭搜索
gossip_file中包括rumor的記錄,並打印第二個字段。


5.比較運算和數值運算


gawk有很多比較運算符,下面列出重要的幾個:
= = 相等
! = 不相等
> 大於
< 小於
> = 大於等於
< = 小於等於
例如:
gawk ‘$4 > 100‘ testfile
將會顯示文件testfile 中那些第四個字段大於1 0 0的記錄。
下表列出了gawk中基本的數值運算符。
運算符說明示例
+ 加法運算2+6
- 減法運算6-3
* 乘法運算2*5
/ 除法運算8/4
^ 乘方運算3^2 (=9)
% 求余數9%4 (=1)
例如:
{print $3/2}
顯示第三個字段被2除的結果。
在gawk中,運算符的優先權和一般的數學運算的優先權一樣。例如:
{print $1+$2*$3}
顯示第二個字段和第三個字段相乘,然後和第一個字段相加的結果。
你也可以用括號改變優先次序。例如:
{print ($1+$2)*$3}
顯示第一個字段和第二個字段相加,然後和第三個字段相乘的結果。


6.內部函數


gawk中有各種的內部函數,現在介紹如下:
6.1 隨機數和數學函數
sqrt(x) 求x 的平方根
sin(x) 求x 的正弦函數
cos(x) 求x 的余弦函數
atan2(x,y) 求x / y的余切函數
log(x) 求x 的自然對數
exp(x) 求x 的e 次方
int(x) 求x 的整數部分
rand() 求0 和1之間的隨機數
srand(x) 將x 設置為rand()的種子數
6.2 字符串的內部函數
· index( in,find) 在字符串in中尋找字符串find 第一次出現的地方,返回值是字符串
find 出現在字符串in 裏面的位置。如果在字符串in 裏面找不到字符串find,則返回值為0。
例如:
print index("peanut"," a n " )
顯示結果3。
· length(string) 求出string 有幾個字符。
例如:
length("abcde")
顯示結果5。
· match(string,regexp) 在字符串string 中尋找符合regexp的最長、最靠左邊的子字符串。返回值是regexp 在string 的開始位置,即index值。match 函數將會設置系統變量
RSTART等於index的值,系統變量RLENGTH 等於符合的字符個數。如果不符合,則會
設置RSTART為0、RLENGTH 為- 1。
· sprintf( format,expression1,. . . ) 和printf 類似,但是sprintf 並不顯示,而是返回字符串。
例如:
sprintf("pi = %.2f (approx.)",2 2 / 7 )
返回的字符串為pi = 3.14 (approx.)
· s u b ( r e g e x p,r e p l a c e m e n t,t a rg e t ) 在字符串t a rget 中尋找符合regexp 的最長、最靠左的
地方,以字串replacement 代替最左邊的r e g e x p。
例如:
str = "water,water,everywhere"
sub( /at/, "ith",str)
結果字符串str會變成wither,water,everywhere
· gsub(regexp,replacement,target) 與前面的sub類似。在字符串target 中尋找符合regexp的所有地方,以字符串replacement 代替所有的regexp。
例如:
str = "water,water,everywhere"
gsub( /at/, "ith",str)
結果字符串str會變成wither,wither,everywhere
· substr(string,start,length)返回字符串string 的子字符串,這個子字符串的長度為length,從第start個位置開始。
例如:
substr("washington",5,3)
返回值為ing
如果沒有length,則返回的子字符串是從第start 個位置開始至結束。
例如:
substr("washington",5)
返回值為ington。
· tolower(string) 將字符串s t r i n g的大寫字母改為小寫字母。
例如:
tolower("MiXeD cAsE 123")
返回值為mixed case 123。
· toupper(string) 將字符串s t r i n g的小寫字母改為大寫字母。
例如:
toupper("MiXeD cAsE 123")
返回值為MIXED CASE 123。
6.3 輸入輸出的內部函數
· close(filename) 將輸入或輸出的文件filename 關閉。
· system(command) 此函數允許用戶執行操作系統的指令,執行完畢後將回到gawk程序。
例如:
BEGIN {system("ls")}


7.字符串和數字


字符串就是一連串的字符,它可以被gawk逐字地翻譯。字符串用雙引號括起來。數字不能用雙引號括起來,並且gawk將它當作一個數值。例如:
gawk ‘$1 != "Tim" {print}‘ testfile
此命令將顯示第一個字段和Tim不相同的所有記錄。如果命令中Tim兩邊不用雙引號,gawk將不能正確執行。
再如:
gawk ‘$1 == "50" {print}‘ testfile
此命令將顯示所有第一個字段和5 0這個字符串相同的記錄。g a w k不管第一字段中的數值
的大小,而只是逐字地比較。這時,字符串5 0和數值5 0並不相等。


8.格式化輸出


我們可以讓動作顯示一些比較復雜的結果。例如:
gawk ‘$1 != "Tim" {print $1,$ 5,$ 6,$2}‘ testfile
將顯示testfile文件中所有第一個字段和Ti m不相同的記錄的第一、第五、第六和第二個字段。
進一步,你可以在p r i n t動作中加入字符串,例如:
gawk ‘$1 != "Tim" {print "The entry for ",$ 1,"is not Tim. ",$2}‘ testfile
print動作的每一部分用逗號隔開。
借用C語言的格式化輸出指令,可以讓gawk的輸出形式更為多樣。這時,應該用printf而不是print。例如:
{printf "%5s likes this language/n",$ 2 }
printf中的%5s 部分告訴gawk如何格式化輸出字符串,也就是輸出5個字符長。它的值由printf 的最後部分指出,在此是第二個字段。/n是回車換行符。如果第二個字段中存儲的是人名,則輸出結果大致如下:
Tim likes this language
Geoff likes this language
Mike likes this language
Joe likes this language
gawk 語言支持的其他格式控制符號如下:
· c 如果是字符串,則顯示第一個字符;如果是整數,則將數字以ASCII 字符的形式顯示。
例如:
printf “% c”,65
結果將顯示字母A。
· d 顯示十進制的整數。
· i 顯示十進制的整數。
· e 將浮點數以科學記數法的形式顯示。
例如:
print “$ 4 . 3 e”,1950
結果將顯示1.950e+03。
· f 將數字以浮點的形式顯示。
· g 將數字以科學記數法的形式或浮點的形式顯示。數字的絕對值如果大於等於0 . 0 0 0 1則
以浮點的形式顯示,否則以科學記數法的形式顯示。
· o 顯示無符號的八進制整數。
· s 顯示一個字符串。
· x 顯示無符號的十六進制整數。1 0至1 5以a至f表示。
· X 顯示無符號的十六進制整數。1 0至1 5以A至F表示。
· % 它並不是真正的格式控制字符,% %將顯示%。
當你使用這些格式控制字符時,你可以在控制字符前給出數字,以表示你將用的幾位或幾個字符。例如,6 d表示一個整數有6位。再請看下面的例子:
{printf "%5s works for %5s and earns %2d an hour",$1,$2,$3}
將會產生類似如下的輸出:
Joe works for Mike and earns 12 an hour
當處理數據時,你可以指定數據的精確位數
{printf "%5s earns $%.2f an hour",$ 3,$ 6 }
其輸出將類似於:
Joe earns $12.17 an hour
你也可以使用一些換碼控制符格式化整行的輸出。之所以叫做換碼控制符,是因為gawk對這些符號有特殊的解釋。下面列出常用的換碼控制符:
/a 警告或響鈴字符。
/b 後退一格。
/f 換頁。
/n 換行。
/r 回車。
/t Ta b。
/v 垂直的t a b。


9.改變字段分隔符


在g a w k中,缺省的字段分隔符一般是空格符或TA B。但你可以在命令行使用- F選項改變字符分隔符,只需在- F後面跟著你想用的分隔符即可。
gawk -F" ;"‘/tparker/{print}‘ /etc/passwd
在此例中,你將字符分隔符設置成分號。註意: - F必須是大寫的,而且必須在第一個引號之前。


10.元字符


gawk語言在格式匹配時有其特殊的規則。例如, cat能夠和記錄中任何位置有這三個字符的字段匹配。但有時你需要一些更為特殊的匹配。如果你想讓cat只和concatenate匹配,則需要在格式兩端加上空格:
/ cat / {print}
再例如,你希望既和cat又和CAT匹配,則可以使用或(|):
/ cat | CAT / {print}
在gawk中,有幾個字符有特殊意義。下面列出可以用在gawk格式中的這些字符:
· ^ 表示字段的開始。
例如:
$3 ~ /^b/
如果第三個字段以字符b開始,則匹配。
· $ 表示字段的結束。
例如:
$3 ~ /b$/
如果第三個字段以字符b結束,則匹配。
· . 表示和任何單字符m匹配。
例如:
$3 ~ /i.m/
如果第三個字段有字符i,則匹配。
· | 表示“或”。
例如:
/ c a t | C AT/
和cat 或C AT字符匹配。
· * 表示字符的零到多次重復。
例如:
/UNI*X/
和U N X、U N I X、U N I I X、U N I I I X等匹配。
· + 表示字符的一次到多次重復。
例如:
/UNI+X/
和U N I X、U N I I X等匹配。
· /{a,b/} 表示字符a次到b次之間的重復。
例如:
/ U N I / { 1,3 / } X
和U N I X、U N I I X和U N I I I X匹配。
· ? 表示字符零次和一次的重復。
例如:
/UNI?X/
和UNX 和U N I X匹配。
· [] 表示字符的範圍。
例如:
/I[BDG]M/
和I B M、I D M和I G M匹配
· [^] 表示不在[ ]中的字符。
例如:
/I[^DE]M/
和所有的以I開始、M結束的包括三個字符的字符串匹配,除了IDM和IEM之外。


11.調用gawk程序


當需要很多對模式和動作時,你可以編寫一個gawk程序(也叫做gawk腳本)。在gawk程序中,你可以省略模式和動作兩邊的引號,因為在gawk程序中,模式和動作從哪開始和從哪結束時是很顯然的。
你可以使用如下命令調用g a w k程序:
gawk -f scrīpt filename
此命令使gawk對文件filename執行名為scrīpt的gawk程序。
如果你不希望使用缺省的字段分隔符,你可以在f選項後面跟著F選項指定新的字段分隔符(當然你也可以在gawk程序中指定),例如,使用分號作為字段分隔符:
gawk -f scrīpt -F";" filename
如果希望gawk 程序處理多個文件,則把各個文件名羅列其後:
gawk -f scrīpt filename1 filename2 filename3 ...
缺省情況下, gawk的輸出將送往屏幕。但你可以使用Linux的重定向命令使gawk的輸出送往一個文件:
gawk -f scrīpt filename > save_file


12.BEGIN和END


有兩個特殊的模式在gawk中非常有用。BEGIN模式用來指明gawk開始處理一個文件之前執行一些動作。BEGIN經常用來初始化數值,設置參數等。END模式用來在文件處理完成後執行一些指令,一般用作總結或註釋。
BEGIN 和END中所有要執行的指令都應該用花括號括起來。BEGIN 和END必須使用大寫。
請看下面的例子:
BEGIN { print "Starting the process the file" }
$1 == "UNIX" {print}
$2 > 10 {printf "This line has a value of %d",$ 2 }
END { print "Finished processing the file. Bye!"}
此程序中,先顯示一條信息: Starting the process the file,然後將所有第一個字段等於
UNIX的整條記錄顯示出來,然後再顯示第二個字段大於10 的記錄,最後顯示信息: Finished processing the file. Bye!


13.變量


在gawk中,可以用等號( = )給一個變量賦值:
var1=10
在gawk中,你不必事先聲明變量類型。
請看下面的例子:
$1 == "Plastic" { count = count + 1 }
如果第一個字段是Plastic,則count的值加1。在此之前,我們應當給count賦予過初值,一般是在BEGIN部分。
下面是比較完整的例子:
BEGIN { count = 0 }
$5 == "UNIX" { count = count + 1 }
END { printf "%d occurrences of UNIX were found",count }
變量可以和字段和數值一起使用,所以,下面的表達式均為合法:
count = count + $6
count = $5 - 8
count = $5 + var1
變量也可以是格式的一部分,例如:
$2 > max_value {print "Max value exceeded by ",$2 - max_value}
$4 - var1 < min_value {print "Illegal value of ",$ 4 }


14.內置變量


gawk語言中有幾個十分有用的內置變量,現在列於下面:
NR 已經讀取過的記錄數。
FNR 從當前文件中讀出的記錄數。
FILENAME 輸入文件的名字。
FS 字段分隔符(缺省為空格)。
RS 記錄分隔符(缺省為換行)。
OFMT 數字的輸出格式(缺省為% g)。
OFS 輸出字段分隔符。
ORS 輸出記錄分隔符。
NF 當前記錄中的字段數。
如果你只處理一個文件,則NR 和FNR 的值是一樣的。但如果是多個文件, NR是對所有的文件來說的,而FNR 則只是針對當前文件而言。
例如:
NR <= 5 {print "Not enough fields in the record"}
檢查記錄數是否小於5,如果小於5,則顯示出錯信息。
FS十分有用,因為FS控制輸入文件的字段分隔符。例如,在BEGIN格式中,使用如下的命令:
F S = " : "


15. 控制結構


15.1 if 表達式
if 表達式的語法如下:
if (expression){
c o m m a n d s
}
e l s e {
c o m m a n d s
}
例如:
# a simple if loop
(if ($1 == 0){
print "This cell has a value of zero"
}
else {
printf "The value is %d/n",$ 1
} )
再看下一個例子:
# a nicely formatted if loop
(if ($1 > $2){
print "The first column is larger"
}
else {
print "The second column is larger"
} )
15.2 while 循環
while 循環的語法如下:
while (expression){
c o m m a n d s
}
例如:
# interest calculation computes compound interest
# inputs from a file are the amount,interest_rateand years
{var = 1
while (var <= $3) {
printf(" %f/n",$1*(1+$2)^var)
var++}
}
15.3 for 循環
for 循環的語法如下:
for (initialization; expression; increment) {
c o m m a n d
}
例如:
# interest calculation computes compound interest
# inputs from a file are the amount,interest_rateand years
{for (var=1; var <= $3; var++) {
printf(" %f/n",$1*(1+$2)^var)
}
}
5.4 next 和exit
next 指令用來告訴gawk 處理文件中的下一個記錄, 而不管現在正在做什麽。語法如下:
{ command1
c o m m a n d 2
c o m m a n d 3
next
c o m m a n d 4
}
程序只要執行到n e x t指令,就跳到下一個記錄從頭執行命令。因此,本例中, c o m m a n d 4指令永遠不會被執行。
程序遇到exit指令後,就轉到程序的末尾去執行END,如果有END的話。


16.數組


gawk語言支持數組結構。數組不必事先初始化。聲明一個數組的方法如下:
a r r a y n a m e [ n u m ] = v a l u e
請看下面的例子:
# reverse lines in a file
{line[NR] = $0 } # remember each line
END {var=NR # output lines in reverse order
while (var > 0){
print line[var]
v a r - -
}
}
此段程序讀取一個文件的每一行,並用相反的順序顯示出來。我們使用NR作為數組的下標來存儲文件的每一條記錄,然後在從最後一條記錄開始,將文件逐條地顯示出來。


17.用戶自定義函數


復雜的gawk 程序常常可以使用自己定義的函數來簡化。調用用戶自定義函數與調用內部函數的方法一樣。函數的定義可以放在gawk 程序的任何地方。
用戶自定義函數的格式如下:
function name (parameter-list) {
b o d y - o f - f u n c t i o n
}
name 是所定義的函數的名稱。一個正確的函數名稱可包括一序列的字母、數字、下標線(underscores),但是不可用數字做開頭。parameter- list 是函數的全部參數的列表,各個參數之間以逗點隔開。body-of-function 包含gawk 的表達式,它是函數定義裏最重要的部分,它決定函數實際要做的事情。
下面這個例子,會將每個記錄的第一個字段的值的平方與第二個字段的值的平方加起來。
{print "sum =",S q u a r e S u m ( $ 1,$ 2 ) }
function SquareSum(x,y) {
s u m = x * x + y * y
return sum
}
到此,我們已經知道了gawk的基本用法。gawk語言十分易學好用,例如,你可以用gawk編寫一段小程序來計算一個目錄中所有文件的個數和容量。如果用其他的語言,如C語言,則會十分的麻煩,相反,gawk只需要幾行就可以完成此工作。


18.幾個實例


最後,再舉幾個gawk的例子:
gawk ‘{if (NF > max) max = NF}
END {print max}‘
此程序會顯示所有輸入行之中字段的最大個數。
gawk ‘length($0) > 80‘
此程序會顯示出超過80 個字符的每一行。此處只有模式被列出,動作是采用缺省值顯示整個記錄。
gawk ‘NF > 0‘
顯示擁有至少一個字段的所有行。這是一個簡單的方法,將一個文件裏的所有空白行刪除。
gawk ‘BEGIN {for (i = 1; i <= 7; i++)
print int(101 * rand())}‘
此程序會顯示出範圍是0 到100 之間的7 個隨機數。
ls -l files | gawk ‘{x += $4}; END {print "total bytes: " x}‘
此程序會顯示出所有指定的文件的總字節數。
expand file | gawk ‘{if (x < length()) x = length()}
END {print "maximum line length is " x}‘
此程序會將指定文件裏最長一行的長度顯示出來。expand會將tab改成space,所以是用實際的右邊界來做長度的比較。
gawk ‘BEGIN {FS = ":"}
{print $1 | "sort"}‘ /etc/passwd
此程序會將所有用戶的登錄名稱,依照字母的順序顯示出來。
gawk ‘{nlines++}
END {print nlines}‘
此程序會將一個文件的總行數顯示出來。
gawk ‘END {print NR}‘
此程序也會將一個文件的總行數顯示出來,但計算行數的工作由gawk來做。
gawk ‘{print NR,$ 0 } ‘
此程序顯示出文件的內容時,會在每行的最前面顯示出行號,它的函數與‘cat -n’類似。

linux 之awk命令詳解