awk的應用
awk是Linux文本處理三劍客之一,是一款強大的報告生成器,不同於sed和grep,它的側重點是如何把文本信息更好的展示出來,常用與統計和格式化輸出。awk相當於微型的shell,有着自己一套語法結構,例如:循環結構,數組,條件判斷,函數,內置變量等功能。處理對象一般純文本文件或純文本信息。在開源界的awk是gawk(GNU)。在Linux中常使用的gawk,但是一般都稱之爲awk。
基本用法:
gawk [options] 'program' file[ file ...]
program : PATTERN { ACTION STATEMENT }
由語句組成,語句分隔符是;
ACTION: print, printf
file[ file...]:待處理的輸入文件,可以是一個也可以是多個
gawk執行命令時,是一行一行進行處理文件file[ file ...]的。讀取一行後,根據指定的分隔符將每行進行分割存儲在一個動態數組中,然後根據pattern進行匹配,匹配了則執行 action statement操作,不匹配則不執行action statement。處理完這一行後,接着讀取下一行,再進行同樣的流程,知道所有文件都讀取完。
一、option選項:
可以使用兩種形式:-POSIX one letter, --GNU-style long options。主要有:
-F fs,--field-separator fs
指明分隔符fs
-v var=val,--assign var=val
給變量賦值。可以賦值的變量在BEGIN中介紹
-f program-file,--file program-file
從program-file文件中讀取執行程序代碼,可以使用多個-f或--file讀取多個程序代碼文件
-W dump-variables[=file],--dump-variables[=file]
打印文件[file]中的全局變量列表,包括變量名,類型,最後的賦值。沒有指明文件時則爲當前工作目錄的的awkvars.out文件。在定義了較多函數時可以用來有效避免在需要用本地變量時用了全局變量,如for循環中的i、j等。
二、program:pattern { action statements }
pattern表示在處理文件時對對取得的每一行做匹配,匹配到就用後面的action進行處理,爲匹配到就不用後面的action處理。 pattern與action可以缺一個,但不能同時缺少。當pattern缺失時,默認是匹配每一行;當action缺失時,默認爲 { print }。
statement可以有多個,中間用分號分隔。
(一)Patterns有如下幾種:
BEGIN
END
/regular expression/
relational expression
pattern && pattern
pattern || pattern
pattern ? pattern : pattern
(pattern) 分組
! pattern 取反
pattern1, pattern2
(1)BEGIN/END: 特殊模式
BEGIN 和END:BEGIN在文件輸入前執行一遍,END是在文件讀入後執行一遍。這兩者後面都必須跟action。
(2) relational expression:關係表達式,有真假之分,一般來說,其結果爲非0或非空字符串時爲“真”,否則,爲“假”;
(3) &&, ||, and ! :邏輯或,與,非
(4)pattern1 ? pattern2 : pattern3
當pattern1爲真時,用pattern2進行匹配,否則用pattern3進行匹配
(3) ‘pattern1, pattern2’:範圍匹配,‘line ranges’,可用sed或vim的地址定界法;startline, endline
(4) /regular expression/:正則表達式
. 匹配任意字符,包括換行符
^ 行首定位符
$ 行尾定位符
[abc...] 匹配 abc....中任意一個
[^abc...] 不匹配 abc....中任意一個
r1|r2 匹配 r1 或 r2,任選一
r1r2 先匹配 r1,然後接着匹配 r2,也即必須連續匹配r1r2
r+ 多次匹配r
r* 匹配任意次r
r? 匹配0次或1次r
(r) 分組
r{n} 精確匹配n次r
r{n,} 至少匹配n次r
r{n,m} 匹配n到m次r
\y 詞首或者詞尾定位符
\B 詞內匹配定位符(即不能是詞首或詞尾)
\< 詞首定位符
\> 詞尾定位符
\w 匹配文件名的組成字符 (字母、數字、下劃線三者之一)
\W 匹配非文件名組成字符
[:alnum:] 字母數字字符
[:alpha:] 字母字符
[:blank:] 空白字符Space or tab characters.
[:cntrl:] 控制字符
[:digit:] 數字字符
[:graph:] 可視打印字符(不包括空格)
[:lower:] 小寫字母字符
[:print:] 可打印非控制字符
[:space:] 空白字符 (包括 space, tab)
[:upper:] 大寫字母字符
[:xdigit:] 十六進制字符
(二)action處理動作
對前面pattern的匹配結果處理動作主要有兩種:print和printf。
The print Statement:
print 打印輸出當前行,結尾爲換行符
print expr-list 打印輸出列表,列表之間可用空格或者逗號分隔,空格表示連續打印,逗號表示以OFS分隔(默認爲空格,除非前面option中指定了)
print expr-list >file 將表達式列表輸出至文件file
print ... >> file 將打印列表追加至文件file
print ... | command 將打印列表通過管道送給下一個命令
The printf Statement:
printf fmt, expr-list 將表達式列表格式化並輸出打印至屏幕
printf fmt, expr-list >file 將表達式列表格式化後輸出至文件file
(1) fmt,format,是必須的,表示格式化輸出;
(2) 不會自動換行,需顯式給定行分隔符(\n);
(3) format中需要分別爲後面的每個item指定一個格式符
格式符format:都以%開頭,後跟一個字符
%c: 顯示字符的ASCII碼;
%d,%i: 顯示十進制整數;
%e, %E: 科學計數法顯示數值;
%f: 顯示爲浮點數;float
%g, %G: 以科學計數法格式或浮點數格式顯示數值;
%s: 字符串
%u: 無符號的整數
%%: 顯示%自身
修飾符:
#[.#]: 第一個#指定顯示寬度,例如%30s;第二個#表示小數點後的精度;
-: 左對齊
+:顯示數值符號
(三)statement中可用的元素:
1、變量:
(1)built-in variables內建變量:
IGNORECASE 設成非0值時,忽略gawk命令中出現的字符串的字符大小寫
OFMT 數字的輸出格式;默認值爲%.6 g
FS: input field seperator,輸入字段分隔符,默認爲空白字符;
如果沒有指明,且沒有空格分隔,或者指明的分隔符航中不存在,則整行爲一個字段
RS:input record separator,輸入行分隔符, 默認爲換行符;
OFS: output field seperator,輸出字段分隔符,默認爲空白字符;
ORS:output record separator, 輸出行分隔符,默認爲換行符;
NF: number of field in current record,當前行的字段數;
NR:The total number of input records seen so far已處理的輸入數據行數目,所有文件統一計數;
FNR:The input record number in the current input file已處理的輸入數據行數目,各文件分別計數;
ARGC:命令行參數的個數;不包括program
ARGV:數組,保存了命令行參數;
ARGIND 當前文件在ARGV中的位置
ENVIRON 當前shell環境變量及其值組成的關聯數組
FILENAME 用作gawk輸入數據的數據文件的文件名
(2) 自定義變量
-v var=val:
變量名區分字符大小寫
定義變量的位置:
(1) 可以program中定義變量,可以在{}中定義,定義後與其他body用分號隔開;
(2) 通過-v選項定義變量;
2、String Constants字符串常量:只能在“”中起作用
\\ 左斜線
\b 刪除
\n 換行
\t 水平tab鍵
\v 豎tab
3、operator操作符:
算術操作符:
x+y, x-y, x*y, x/y, x^y, x%y
-x: 負值
+x: 轉換爲數值
字符串操作:字符串連接,即加個空格
賦值操作符:
=, +=, -=, *=, /=, %=, ^=
++, --
比較操作符:
>, >=, <. <=, ==, !=
模式匹配符:
~
!~
例子:~]# awk -F: '{$3>=500?usertype="common user":usertype="sysuser or admin";printf "%20s:%-s\n",$1,usertype}' /etc/passwd
4、函數調用:
function_name(argu1,argu2,...)
① String Functions字符串函數
asort(s [, d]) 將數組s按數據元素值排序。索引值會被替換成表示新的排序順序的連續數字。如果指定了d,則排序後的數組回存儲在數組d中
asorti(s [, d]) 將數組s按索引值排序。生成的數組會將索引值作爲數據元素值,用連續數組索引來表明排序順序。如果指定了d,排序後的數組回存儲在數組d中
gensub(r, s, h [, t]) 查找變量$0或目標字符串t(如果提供了的話)來匹配正則表達式r。如果h是一個以g或者G開頭的字符串,就用s替換掉匹配的文本。如果h是一個數字,它表示要替換掉第幾處r匹配的地方
gsub(r, s, [, t]) 查找變量$0或目標字符串t(如果提供了的話)來匹配正則表達式r。如果找到了,就全部替換成字符串s
index(s, t) 返回字符串t在字符串s中的索引值,如果沒找到的話,返回0
length([s]) 返回字符串s的長度,如果沒有指定的話,返回$0的長度
match(s, r [, a]) 返回字符串s中正則表達式r出現位置的索引。如果指定了數組a,它會存儲s中匹配正則表達式的那部分
split(s, a [, r]) 將s用FS字符或正則表達式r(如果指定了的話),分開放到數組a中。返回字段的總數
sprintf(format, variables) 用提供的format和variables返回一個類似於printf輸出的字符串
sub(r, s [, t]) 在變量$0或目標字符串t中查找正則表達式r的匹配。如果找到了,就用字符串s替換掉第一處匹配
substr(s, i [, n]) 返回s中從索引值i開始的n個字符組成的子字符串。如果未提供n,則返回s剩下的部分
tolower(s) 將s的所有字符轉換成小寫
toupper(s) 將s的所有字符轉換成大寫
② Time Functions時間函數
mktime(datespec) 將一個YYYY MM DD HH MM SS [DST]格式置頂的日期轉換徹骨時間戳(時間戳指:自1970-01-01 00:00:00 UTC到現在,以秒爲單位的計數,通常稱epoch time)
strftime(format [, timestamp]) 將當前時間的時間戳huotimestamp(如果提供了的話)轉化成用shell函數格式date()的格式化日期
systime() 返回當前時間的時間戳(同上面的時間戳)
③ USER-DEFINED FUNCTIONS自定義函數
必須使用function關鍵字
function name([variables])
{
statements
}
可以使用return語句返回值
使用自定義函數定義函數時,必須在所有代碼塊之前,包括BEGIN
# gawk 'function test(v1){print v1} BEGIN{ test("abc") } '
abc
5、Control Statements控制語句:
控制語句有如下幾種:
if (condition) statement [ else statement ]
while (condition) statement
do statement while (condition)
for (expr1; expr2; expr3) statement
for (var in array) statement
break
continue
delete array[index]
delete array
exit [ expression ]
{ statements }
①、if-else
語法:if (condition) statement [ else statement ]
if (condition) { statements; } [ else { statements; }]
# awk -F: '{if ($3>=500) print $1," is a common user." }' /etc/passwd
# awk -F: '{if ($3>=500) {print $1," is a common user."} else {print $1," is a system user or admin."}}' /etc/passwd
# awk '{if (NF>6) print NF, $0 }' /etc/inittab
用法:對awk取得的整行或行中的字段做條件判斷;
②、 while循環
語法:while (condition) statement
while (condition) { statements }
條件爲真時進入循環,直到爲假退出;
用法:通常用於在當前行的各字段間進行循環;
# awk '{i=1;while(i<=NF){printf "%20s:%d\n",$i,length($i); i++}}' /etc/inittab
# awk '{i=1;while(i<=NF){if (length($i)>5) {printf "%20s:%d\n",$i,length($i);} i++}}' /etc/inittab
③、 do-while循環
語法:do statement while (condition)
do { do-while-body } while (condition)
意義:至少執行一次循環體do-while-body再進入循環體;
④、 for循環
語法: for (expr1; expr2; expr3) statement
for (expr1; expr2; expr3) { statements }
for (varaiable assignment; condition; iteration process) { for-body }
# awk '{for(i=1;i<=NF;i++) {printf "%s:%d\n", $i, length($i)}}' /etc/inittab
# awk '{for (i=1;i<=NF;i++) {if (length($i)>=5) {printf "%20s:%d\n",$i,length($i)}} }' /etc/inittab
for循環在awk中有一個專用於遍歷數組元素:
語法:for (var in array) { for-body }
⑤、 switch:這個是gawk所特有的
語法:switch (expression) {case VALUE or /REGEXP/: statement; ...; default: statementN}
比如: switch ($3) {case xy : ;case mn: ; default: }
⑥、 break and continue
break [n]: 退出當前循環
continue:提前結束本輪循環,直接進入下輪循環
⑦、 next
提前結束對本行的處理而進入下一行的處理
# awk -F: '{if($3%2!=0) next;print $1,$3}' /etc/passwd
6、Array關聯數組:
array[index-expression]
index-expression:
可以使用任意字符串;
如果某數組元素事先不存在,在引用時,awk會自動創建此元素並將其值初始化爲空串;
因此,若要判斷數組是否存在某元素,要使用“index in array”進行;
要遍歷數組中的每個元素,使用: for (var in array) { for body }
注意:var會遍歷array的每一個索引,print array[var]
例子:統計每一行中各單詞分別出現的次數
# awk '{for(i=1;i<=NF;i++) {count[$i]++}}END{for(j in count) {print j,count[j]}}' awk.txt
把每一個單詞當作一個事先存在的索引的下標,當這個單詞對應的元素的值是已經出現的次數,我們讓awk去遍歷這個字段在我們的下標索引中是否存在,存在就加1。表達式中,count[$i]是以單詞作爲索引的數組,沒有賦值則爲空,++後就爲至少爲1了,在遍歷單詞時,如果輪到這個單詞又出現了,則會++,值加1,輪到出現新的單詞時,則記爲新數組索引,其值++。
# ss -tan | awk '!/^State/{state[$1]++}END{for (i in state) {print i,state[i]}}'
# netstat -tan | awk '/^tcp/{state[$NF]++}END{for(i in state){print i,state[i]}}'
統計httpd訪問日誌中,每個IP出現的次數;
# awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' /var/log/httpd/access_log
下面是操作截圖: