文字處理三劍客之awk(原創)
AWK是一種優良的文字處理工具,Linux及Unix環境中現有的功能最強大的資料處理引擎之一。這種程式設計及資料操作語言(其名稱得自於它的創始人阿爾佛雷德·艾侯(Alfred Aho)、彼得·溫伯格(Peter Jay Weinberger)和布萊恩·柯林漢(Brian Wilson Kernighan)姓氏的首個字母)的最大功能取決於一個人所擁有的知識。AWK提供了極其強大的功能:可以進行正則表示式的匹配,樣式裝入、流控制、數學運算子、程序控制語句甚至於內建的變數和函式。它具備了一個完整的語言所應具有的幾乎所有精美特性。實際上AWK的確擁有自己的語言:AWK程式設計語言,三位建立者已將它正式定義為“樣式掃描和處理語言”。它允許您建立簡短的程式,這些程式讀取輸入檔案、為資料排序、處理資料、對輸入執行計算以及生成報表,還有無數其他的功能。gawk是AWK的GNU版本。最簡單地說,AWK是一種用於處理文字的程式語言工具。AWK在很多方面類似於Unix shell程式語言,儘管AWK具有完全屬於其本身的語法。它的設計思想來源於SNOBOL4、sed、Marc Rochkind設計的有效性語言、語言工具yacc和lex,當然還從C語言中獲取了一些優秀的思想。在最初創造AWK時,其目的是用於文字處理,並且這種語言的基礎是,只要在輸入資料中有模式匹配,就執行一系列指令。該實用工具掃描檔案中的每一行,查詢與命令列中所給定內容相匹配的模式。如果發現匹配內容,則進行下一個程式設計步驟。如果找不到匹配內容,則繼續處理下一行。
在我們的Linux中,awk其實是gawk的一個軟連線,所以我們所使用的都是GNU AWK:
awk的執行方式:
(1) awk命令列
# awk
(2) awk程式檔案
# awk -f /path/from/awk_script
(3) awk指令碼
#!/bin/awk –f
Awk在處理文字時會將每一行讀取到自己的記憶體空間當中,並將每一行文字根據我們制定的分隔符等分成不同的區域,類似指令碼的內建位置變數。在處理完之後,輸出時awk依然是按照一行來輸出的,即便是輸出的內容不是原來的一整行,輸出的分割符預設為空格,也可以自定義。
基本用法:
gawk [OPTIONS] 'program' FILE1 FILE2 ...
program: PATTERN{ACTION STATEMENT}
由語句組成,各語句間使用分號分隔;
ACTION: print, printf
選項:
-F[]: 指明輸入欄位分隔符,可以是多個,例:-F [:,]即用:當分隔符又用,當分隔符;
-v VAR_NAME=VALUE: 變數賦值;
-f /PATH/FROM/AWK_SCRIPT:
1、awk的輸出命令之一:print
用法:print item1, item2, ...
item:
字串:用引號引用;
print "hello", "world"
變數:顯示變數的值;
print name
引用變數:直接使用變數名
數值:無須加引號
要點:
(1) 各item之間需要使用逗號分隔;而輸出時的分隔符為預設為空白字元;
(2) 輸出的各item可以為字串或數值、當前記錄的欄位($#)、變數或awk的表示式;數值會被隱式轉換為字串進行輸出;
(3) print後面的item省略時,相當於執行“print $0”,用於輸出整行;
(4) 輸出空白字元:print " "
2、變數
內建變數,自定義變數
2.1 內建變數
AWK的內建變數包括域變數,例如$1, $2, $3,以及$0。這些變數給出了記錄中域的內容。 內建變數也包括一些其他變數:
NR:已輸入記錄的條數。
FNR:行數,各檔案單獨計數。
NF:當前記錄中域的個數。記錄中最後一個域可以以$NF的方式引用。print NF 每行有幾個欄位,print $NF 顯示每一行的最後一個欄位
FILENAME:當前輸入檔案的檔名。
FS:“域分隔符”,用於將輸入記錄分割成域。其預設值為“空白字元”,即空格和製表符。FS可以替換為其它字元,從而改變域分隔符。-v FS="[ ,:.]"
RS:當前的“記錄分隔符”。預設狀態下,輸入的每行都被作為一個記錄,因此預設記錄分隔符是換行符。
OFS:“輸出域分隔符”,即分隔print命令的引數的符號。其預設值為空格。
ORS:“輸出記錄分隔符”,即每個print命令之間的符號。其預設值為換行符。
OFMT:“輸出數字格式”(Format for numeric output),其預設值為"%.6g"。
ARGC:awk命令列中的引數的個數。
ARGV:陣列,儲存了命令列引數本身。
2.2 自定義變數
(1) -v VAR_NAME=VALUE
變數名區分字元大小寫;
(2) 在program中自定義變數
# awk 'BEGIN{FS=":";f1=3}{print $f1}' /etc/passwd
f1賦值一次一直使用
每讀取一行f1賦一次值
3、awk的輸出命令之二:printf
語法:printf FORMAT(格式),item1,item2,...
要點:
(1) 必須提供FORMAT;
(2) 與print語句不同,printf不會自動換行,需要顯式指定換行符:\n
(3) FORMAT中需要分別為後面的每個item指定一個格式符,否則item則無法顯示;
格式符:都以%開頭,後跟單個字元;
%c: 顯示字元的ASCII碼;
%d, %i:顯示為十進位制整數;
%e, %E: 科學計數法顯示數值;
%f:顯示為浮點數;
%g, %G:以科學計數法或浮點數格式顯示數值;
%s: 顯示為字串;
%u:顯示無符號整數;
%%: 顯示%符號自身;
修飾符:
#[.#]:
左邊的#:用於指定顯示寬度;
右邊的#: 顯示精度;
+:顯示數值符號
-:左對齊
4、操作符
算術操作符:
x+y, x-y, x*y, x/y, x^y, x%y
-x: 負值
+x: 轉換為數值
字元操作符:字串連線
賦值操作符:
=, +=, -=, *=, /=, %=, ^=
++, --
比較操作符:
>, >=, <, <=, ==, !=
模式匹配操作符:
~:是否能由右側指定的模式所匹配;
!~:是否不能由右側指定模式所匹配;
邏輯操作符:
&&:與運算
||: 或運算
條件表示式:
selector?if-true-expression:if-false-expression
# awk -F: '{$3>=500?usertype="Common User":usertype="Sysadmin or Sysuser";printf "%15s:%-s\n",$1,usertype}' /etc/passwd
函式呼叫:
function_name(argu1, argu2, ...)
5、PATTERN模式
(1) empty:空模式,匹配所有行;
(2) /Regular Expression/(正則表示式):僅將ACTION應用於能夠被Regular Expression所匹配到的行;
例如:awk -F: '/^[ab]/{print $1,$3}' /etc/passwd
(3) relational expression:關係表示式,即結果為“真”、“假”的表示式, 或者其結果能類同於“真”或“假”的表達;一般來說,其結果為非0數值或非空字串即可類同為“真”,否則,則類同為“假”;
例如:awk -F: '$3>=500{print $1,$3}' /etc/passwd
例如:awk -F: '$1~/root/{print $1,$3}' /etc/passwd
(4) line ranges:行範圍,類似sed或vim中的地址定界方式
startline,endline
此方式我沒有試驗成功,待定
(5) BEGIN/END:兩個特殊模式
BEGIN:在檔案格式化操作開始之前事先執行的一次操作;通常用於輸出表頭或做出一個預處理操作;
END:在檔案格式操作完成之後,命令退出之前執行的一次操作;通常用於輸出表尾或做出清理操作;
6、常用ACTION
(1) EXPRESSIONS:例如變數賦值
(2) Control Statements:控制語句,如if, while等;
(3) Compound Statements:複合語句
(4) input statements:輸入語句
(5) output statements:輸出語句
7、控制語句
if (condition) { statements } [else { statement }]
while (condition) { statements }
do statement while (condition)
for(expr1;expr2;expr3) { statements }
for(var in array) { statements }
switch (expression) {case VALUE or /REGEXP/: statement; ...; default: statementN}
break
continue
delete array[index]
delete array
exit [ expression ]
{ statements }
7.1 if-else
語法:if (condition) statement [ else statement ]
if (condition) {statements} [else {statements}]
~]# awk -F: '{if ($3>=500) print $1,$3}' /etc/passwd
~]# awk '{if (NF>=6) print NF,$0}' /etc/rc.d/rc.sysinit
~]# awk -F: '{if ($3>=500) {print $1,"is a common user"} else {print $1,"is a sysadmin or sysuser"}}' /etc/passwd
使用場景:對awk取得的整行或行中的欄位做條件判斷;
7.2 while迴圈
語法:while (condition) statement
while (condition) {statements}
條件為“真”時迴圈,為“假”時退出迴圈;
使用場景:通常用於在當前行的各欄位間進行迴圈;
~]# awk '{i=1;while(i<=NF){if (length($i)>=6) {print $i};i++}}' /etc/issue
7.3 do-while迴圈
語法:do statement while (condition)
do {statements} while (condtion)
意義:至少執行一次迴圈體
7.4 for迴圈
語法:for (expr1;expr2;expr3) statement
for (expr1;expr2;expr3) {statements}
for (variable assignment;codition;iteration process) {for-body}
# awk '{for(i=1;i<=NF;i++){if(length($i)>=6) print $i}}' /etc/issue
第二種用法:用於遍歷陣列中的元素
for (var_name in array) {for-body}
7.5 swtich
語法:switch (expression) {case VALUE or /REGEXP/: statement; ...; default: statementN}
7.6 break and continue
break [n]:退出當前迴圈,n是一個數字,用於指定退出幾層迴圈;
continue:提前結束本輪迴圈而進入下一輪;
7.7 next
提前結束對本行文字的處理,而提前進入下一行的處理操作;
~]# awk -F: '{if($3%2==0) next;print $1,$3}' /etc/passwd
8、陣列
關聯陣列:array[index-expression]
index-expression:
可以使用任意字元;
如果某陣列元素事先不存在,則在引用時,awk會自動建立此元素並將其值初始化為空串;
因此,若要判斷 陣列中的某元素是否存在,要使用“index in array”的方式進行判斷;
要遍歷陣列中的元素,則要使用for (var_name in array)的方式進行;此時,var_name會遍歷array的每個索引,所以,要顯示陣列元素的值,要使用array[var_name]
weekdays
weekdays[mon]="Monday"
weekdays[tue]="Tuesday"
...
for (i in weekdays):此時,i變數會遍歷weekdays陣列的每個索引,即mon, tue,而非元素的值“Monday”或"Tuesday"等;
要獲取元素的值:weekdays[i]
9、函式
內建函式和使用者自定義函式
9.1 內建函式
數值處理:
rand():返回0至1之間的一個隨機數;
字串處理:
length([s]):返回指定的字串的長度;
sub(r,s[,t]):基於r所表示的模式來匹配字串t中的內容,將其第一次被匹配到的內容替換為s所表示的字串;
gsub(r,s[,t]):基於r所表示的模式來匹配字串t中的內容,將其所有被匹配到的內容均替換為s所表示的字串;
split(s,a[,r]):以r為分隔符去切割字串s,並將切割後的結果儲存至a表示的陣列中;
注意:awk的陣列下標從1開始編號
~]# awk '{split($0,userinfo,":");print userinfo[1]}' /etc/passwd
substr(s,i[,n]):從s所表示的字串中取子串,取法:從i表示的位置開始,取n個字元;
時間類函式:
systime():取當前系統時間,結果形式為時間戳;
9.2 使用者自定義函式
function f_name(p,q) {
...
}