1. 程式人生 > >文字處理三劍客之awk(原創)

文字處理三劍客之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) {

                            ...

                   }