gawk 模式處理語言
gawk 工具是一種模式掃描和處理語言,它搜索一個或者多個文件,以查看這些文件中是否存在匹配指定模式的記錄。每次發現匹配時,它通過執行動作的方式來處理文本行。
gawk 屬於數據驅動語言:描述想要處理的數據並告訴gawk 當它發現這些數據時將要做的事情。
語法:
gawk [options] [program] [file-list]
gawk [options] -f program-file [file-list]
program: 用戶在命令行中所包含的gawk 程序,爲了阻止shell 將gawk 命令解釋爲shell 命令,需要使用單引號將其引起來。
program-file: 存放gawk 程序的文件的名稱,將較長或較複雜的程序放置在文件中以減少錯誤和重複輸入。
file-list:gawk 要處理的普通文件的路徑名,這些文件就是輸入文件;如果沒有指定,gawk 將從標準輸入獲取輸入或getline 或協進程指定輸入。
語言基礎
gawk 程序( 來自命令行或者程序文件) 由一行或者多行文本構成,其中包含一個模式和動作,格式如下:
pattern {action}
pattern: 模式,用來從輸入中選取文本行;如果程序行中不包含模式,gawk 將選取輸入中的所有行。
action: 對於由模式選中的每行文本,gawk 工具都執行動作action ;如果程序行中沒有包含動作,gawk 將把選中的行復制到標準輸出。
如果多個模式選中同一行文本,gawk 將按照這些模式出現在程序中的順序分別執行與每個模式關聯的動作。
模式
用斜槓把正則表達式包裝起來,就可以將其作爲模式使用。~ 操作符用於測試某個字段或者變量是否匹配正則表達式。!~ 用於測試不匹配。可以使用布爾操作符||(OR) 或者&&(AND) 來組合任何模式。
BEGIN 和END 兩個獨特的模式,分別是在gawk 開始處理之前和處理之後要執行的命令。
, 逗號是範圍查找符。如果在一個gawk 程序行上用逗號將兩個模式隔開,gawk 選取從匹配第1 個模式的第1 行開始的文本行。gawk 選取的最後一行是匹配第2 個模式的那行緊接着的下一行文本。查找第2 個模式後,將再次查找第1 個模式以再次開始這個過程。
動作
如果gawk 匹配某個模式,它將執行gawk 命令的動作部分所指定的動作。如果沒有指定動作,gawk 將執行默認動作,即print 命令( 可用{print} 顯示錶示) 。除非用逗號將print 命令中的各項區分開,否則gawk 將它們連接起來。逗號使得gawk 用輸出字段分隔符(OFS ,通常是空格符) 將各項分隔開來。
通過用分號將多個動作隔開,可以在同一行上包含多個動作。
註釋
gawk 工具不處理以# 號開頭的程序行中的任何內容。
變量
儘管不需要在使用gawk 變量之前聲明變量,但用戶可以選擇向這些變量賦初值。沒有賦值的數值變量初始化爲0 ,而字符串變量則被初始化爲空串。除了用戶變量之外,gawk 還維護了程序變量。在模式部分和動作部分均可使用用戶變量和程序變量。
程序變量:
變 量 |
含 義 |
$0 |
當前記錄( 作爲單個變量) |
$1-$n |
當前記錄中的字段 |
FILENAME |
當前輸入文件名(null 表示標準輸入) |
FS |
輸入字段分隔符 |
NF |
當前記錄的字段數目 |
NR |
當前記錄的記錄編號 |
OFS |
輸出字段分隔符 |
ORS |
輸出記錄分隔符 |
RS |
輸入記錄分隔符 |
除了可以在程序中初始化變量以外,還可以在命令行上使用-v 選項初始化變量。如果某個變量值在gawk 的兩次運行之間發生改變,這個特性就非常有用。
默認情況下,輸入記錄和輸出記錄的分隔符均爲換行符;輸入字段分隔符是空格和TAB ,輸出字段分隔符是空格。任何時刻均可以修改分隔符的值。
選項
-v
-F
函數
length(str),int(num),index(str1,str2),split(str,arr,del),sprintf(fmt,args),substr(str,pos,len),sub(str,pos,len),tolower(str),toupper(str).
算數操作符
來自C 編程語言。
關聯數組
數組使用字符串作爲索引。在使用關聯數組時,用戶可以用數值字符串作爲索引來模仿傳統數組。
對關聯數組元素賦值
array[string] = value
array: 數組名稱
string: 數組元素在數組中的索引
value: 要指派的值
printf
類似於C 語言中的printf :
printf “control-string”,arg1,arg2,......,argn
control-string: 決定如何格式化參數arg1,arg2,....argn
轉換規格:
%[-][x][.y]conv
'-' 使得參數左對齊,'x' 最小字段寬度,'.y' 數字中小數點右邊的位數,'conv' 數值轉換類型。
控制結構
控制語句將改變gawk 程序中命令的執行順序。
if...else
if(condition)
{commands}
[else
{commands}]
--------------------------
while
while(condition)
{commands}
--------------------------
for
for(init;condition;increment)
{commands}
for(var in array)
{commands}
循環遍歷一個名爲 array 的關聯數組中的所有元素,每次循環,都將 array 的 相應元素的索引值 賦給 var
-------------------------
break
continue
示例:
下面這個示例讀取passwd 文件,列出沒有口令的用戶以及具有重複用戶ID 編號的用戶,注意使用””輸出的字符串信息中排版所使用的空白符,因爲默認情況下使用print 輸出時除非利用',' 分隔字段( 輸出時使用OFS 替代',' 從而將各項分隔開來) ,否則將各個輸出項連接起來。
1 gawk < /etc/passwd '
2 BEGIN {
3 uid[void] =""
4 }
5 {
6 dup = 0
7 split($0,field,":")
8 if(field[2] == "")
9 {
10 if(field[5] == "")
11 {
12 print field[1] " has no password."
13 }
14 else
15 {
16 print field[1] " ("field[5]") has no password."
17 }
18 }
19 for(name in uid)
20 {
21 if(uid[name] == field[3] )
22 {
23 print field[1] " has the same UID as " name " /
24 :UID = " uid[num]
25 dup =1
26 }
27 }
28 if(!dup)
29 {
30 uid[field[1]] = field[3]
31 }
32 }'
33
黑體標示部分表徵了關聯數組的用法,uid 數組使用field[1] 作爲數組的下標索引( 在本例中爲用戶名) ,數組中該索引對應的數組元素的值爲field[3]( 在本例中爲用戶的uid) 。for 循環在每次循環中,將數組uid 的索引值 賦給變量name ,if 判斷利用該索引值對應的數組元素來判斷是否存在uid 重複。
函數split 利用':' 將當前記錄($0 當前記錄作爲當個變量) 分隔到數組field 中。上面提到的用戶名,用戶uid 等都是通過引用數組field 的元素得到的。除了利用split 函數以外,也可以利用給FS( 輸入字段分隔符) 賦值爲”:” 來達到對輸入記錄分隔開來的目的;只不過split 分隔後利用數組元素來引用相應項( 用戶名,uid 等等) ,而利用FS 賦值後可以使用對字段的訪問($1 引用用戶名, 等) 來引用相應項。下面的兩個例子分別使用了上述的兩種方式來對輸入記錄進行處理,其得到的結果相同。
1 gawk < /etc/passwd '
2 BEGIN {
3 }
4 {
5 split($0,field,":")
6 print field[1],field[2],field[3],field[4]
7 }'
8
--------------------
1 gawk < /etc/passwd '
2 BEGIN {
3 FS = ":"
4 }
5 {
6 print $1,$2,$3,$4
7 }'
8
> gawk '{print}' cars
> gawk '/chevy/' cars
> gawk '{print $2,$1}' cars
> gawk '{print $3,$1}' cars
> gawk '/chevy/ {print $3, $1}' cars
> gawk '{ print }' cars
> gawk '{print}' cars
> gawk '/chevy/' cars
> gawk '/chevy/' cars > test.out
> gawk '{print $3,$1}' cars
> gawk '{print $3 $1}' cars
> gawk '{print $3,$1}' cars
> gawk '/chevy/ {print $3,$1}' cars
> gawk '/h/' cars
> gawk '$1 ~ /h/' cars
> gawk '$1 ~ /^h/' cars
> gawk '$2 ~ /^[tm]/ {print $3,$2,"$" $5}' cars
> gawk '$3 ~ /5$/ {print $3,$1,"$" $5}' cars
> gawk '$3 == 1985' cars
> gawk '$5 < 3000' cars
> gawk '$5 <= 3000' cars
> gawk '$1 > 480 {print $2,$3,$4}'
> gawk '$1 > 480 {print $2,$3,$4}' history.out
> gawk '"2000" <= $5 && $5 < "9000"' cars
> gawk '"2000" <= $5 && $5 < "9000"' cars
> gawk '2000 <= $5 && $5 < 9000' cars
> gawk '/volvo/ , /bmw/' cars
> gawk '/volvo/,/bmw/' cars
> gawk '/chevy/,/ford/' cars
> gawk '{print length,$0}' cars | sort -n
> gawk 'length >24 {print NR}' cars
> gawk 'NR == 2 , NR == 4' cars
> gawk 'END {print NR,"cars for sale."}' cars
> gawk 'END {print NR,"cars for sale."} /{print}' cars
> gawk '/mark/ {print}' /etc/passwd
> gawk '/terry/ {print}' /etc/passwd
> history | gawk '{$1 = ">"; print}' | sed -n '/gawk/ p'