概述

AWK是一門解釋型的編程語言。在文本處理領域它是非常強大的,它的名字來源於它的三位作者的姓氏:Alfred Aho, Peter Weinberger 和 Brian Kernighan。

GNU/Linux發佈的AWK目前由自由軟件基金會(FSF)進行開發和維護,通常也稱它為 GNU AWK。

AWK的類型

下面是幾個AWK的變體:

  • AWK – 原先來源於 AT & T 實驗室的的AWK
  • NAWK – AT & T 實驗室的AWK的升級版
  • GAWK – 這就是GNU AWK。所有的GNU/Linux發佈版都自帶GAWK,它與AWK和NAWK完全兼容

AWK的典型用途

使用AWK可以做很多任務,下面是其中一些

  • 文本處理
  • 輸出格式化的文本報表
  • 執行算數運算
  • 執行字符串操作等等

工作流

要成為AWK編程專家,你需要先知道它的內部實現機制,AWK遵循了非常簡單的工作流 – 讀取,執行和重複,下圖描述了AWK的工作流。

包你學會 AWK_正則表達式

Read

AWK從輸入流(文件,管道或者標準輸入)中讀取一行,然後存儲到內存中。

Execute

所有的AWK命令都依次在輸入上執行。默認情況下,AWK會對每一行執行命令,我們可以通過提供模式限制這種行為。

Repeat

處理過程不斷重複,直到到達文件結尾。

程序結構

現在,讓我們先學習一下AWK的程序結構。

BEGIN 語句塊

BEGIN語句塊的語法

BEGIN {awk-commands}

BEGIN語句塊在程序開始的使用執行,它只執行一次,在這裏可以初始化變量。BEGIN是AWK的關鍵字,因此它必須為大寫,注意,這個語句塊是可選的。

BODY 語句塊

BODY語句塊的語法

/pattern/ {awk-commands}

BODY語句塊中的命令會對輸入的每一行執行,我們也可以通過提供模式來控制這種行為。注意,BODY語句塊沒有關鍵字。

END 語句塊

END語句塊的語法

END {awk-commands}

END語句塊在程序的最後執行,END是AWK的關鍵字,因此必須為大寫,它也是可選的。

讓我們創建一個包含序號,學生姓名,科目名稱和得分的文件 marks.txt.

1)  Amit    Physics  80

2)  Rahul   Maths    90

3)  Shyam   Biology  87

4)  Kedar   English  85

5)  Hari    History  89

下面的例子中我們將會顯示文件內容,並且添加每一列的標題

包你學會 AWK_字段_02

上述代碼執行後,輸出以下內容

包你學會 AWK_正則表達式_03

在程序的開始,AWK在BEGIN語句中打印出標題。然後再BODY語句中,它會讀取文件的每一行然後執行AWK的print命令將每一行的內容打印到標準輸出。這個過程會一直重複直到文件的結尾。

基礎語法

AWK的使用非常簡單,我們可以直接在命令行中執行AWK的命令,也可以從包含AWK命令的文本文件中執行。

AWK命令行

我們可以使用單引號在命令行中指定AWK命令

包你學會 AWK_正則表達式_04

AWK程序文件

我們可以使用腳本文件提供AWK命令

包你學會 AWK_正則表達式_05

AWK標準選項

AWK支持下列命令行標準選項

-v 變量賦值選項

該選項將一個值賦予一個變量,它會在程序開始之前進行賦值,下面的例子描述了該選項的使用

包你學會 AWK_字段_06

--dump-variables[=file] 選項

該選項會輸出排好序的全局變量列表和它們最終的值到文件中,默認的文件是 awkvars.out。

包你學會 AWK_字段_07

--help 選項

打印幫助信息.

包你學會 AWK_字段_08

--lint[=fatal] 選項

該選項允許檢查程序的不兼容性或者模稜兩可的代碼,當提供參數 fatal的時候,它會對待Warning消息作為Error。

包你學會 AWK_操作符_09

--posix 選項

該選項開啓嚴格的POSIX兼容。

--profile[=file]選項

該選項會輸出一份格式化之後的程序到文件中,默認文件是 awkprof.out。

包你學會 AWK_正則表達式_10

--traditional 選項

該選項會禁止所有的gawk規範的擴展。

--version 選項

輸出版本號

包你學會 AWK_操作符_11

基本使用示例

本部分會講述一些有用的AWK命令和它們的使用示例,所有的例子都是以下面的文本文件 marks.txt 為基礎的。

包你學會 AWK_操作符_12

在文件marks.txt中,第三列包含了科目名,第四列則是得分,上面的例子中,我們只打印出了這兩列,$3 和 $4 代表了輸入記錄中的第三和第四個字段。

打印所有的行

默認情況下,AWK會打印出所有匹配模式的行

包你學會 AWK_操作符_13

打印匹配模式的列

當模式匹配成功時,默認情況下AWK會打印該行,但是也可以讓它只打印指定的字段。例如,下面的例子中,只會打印出匹配模式的第三和第四個字段。

包你學會 AWK_操作符_14

任意順序打印

包你學會 AWK_正則表達式_15

打印超過18個字符的行

包你學會 AWK_字段_16

內建變量

AWK提供了很多內置的變量,它們在開發AWK腳本的過程中起着非常重要的角色。

標準AWK變量

ARGC 命令行參數個數

命令行中提供的參數個數

包你學會 AWK_操作符_17

ENVIRON 環境變量

環境變量的關聯數組

包你學會 AWK_正則表達式_18

NF 字段數目

包你學會 AWK_操作符_19

OFS 輸出字段分隔符

輸出字段分隔符,默認為空

包你學會 AWK_操作符_20

RSTART

match函數匹配的第一次出現位置

包你學會 AWK_字段_21

$n

當前行中的第n個字段

包你學會 AWK_操作符_22

GNU AWK的變量

ARGIND

當前被處理的ARGV的索引

包你學會 AWK_正則表達式_23

BINMODE

在非POSIX系統上指定對所有的文件I/O採用二進制模式。

ERRORNO

一個代表了getline跳轉失敗或者是close調用失敗的錯誤的字符串。

包你學會 AWK_字段_24

FIELDWIDTHS

設置了空格分隔的字段寬度變量列表的話,GAWK會將輸入解析為固定寬度的字段,而不是使用FS進行分隔。

IGNORECASE

設置了這個變量的話,AWK會忽略大小寫。

包你學會 AWK_正則表達式_25

LINT

提供了對–lint選項的動態控制。

包你學會 AWK_正則表達式_26

操作符

與其它編程語言一樣,AWK也提供了大量的操作符。

算數操作符

算數操作符不多説,直接看例子,無非就是+-*/%

包你學會 AWK_正則表達式_27

增減運算符

自增自減與C語言一致。

包你學會 AWK_操作符_28

賦值操作符

包你學會 AWK_正則表達式_29

關係操作符

包你學會 AWK_字段_30

邏輯操作符

包你學會 AWK_正則表達式_31

三元操作符

包你學會 AWK_正則表達式_32

一元操作符

包你學會 AWK_正則表達式_33

指數操作符

包你學會 AWK_正則表達式_34

字符串連接操作符

包你學會 AWK_操作符_35

數組成員操作符

包你學會 AWK_字段_36

正則表達式操作符

正則表達式操作符使用 ~ 和 !~ 分別代表匹配和不匹配。

包你學會 AWK_正則表達式_37

更多關於正則表達式請看後面的正則表達式部分

正則表達式

AWK在處理正則表達式方面是非常強大的,使用簡單的正則表達式可以處理非常複雜的問題。

包你學會 AWK_操作符_38

包你學會 AWK_操作符_39

數組

AWK支持關聯數組,也就是説,不僅可以使用數字索引的數組,還可以使用字符串作為索引,而且數字索引也不要求是連續的。數組不需要聲明可以直接使用,語法如下:

包你學會 AWK_字段_40

在AWK中,只支持一維數組,但是可以通過一維數組模擬多維,例如我們有一個3×3的三維數組

包你學會 AWK_正則表達式_41

流程控制

流程控制語句與大多數語言一樣,基本格式如下

包你學會 AWK_字段_42

循環

循環操作與其他C系語言一樣,主要包括 for,while,do...while,break,continue 語句,當然,還有一個 exit語句用於退出腳本執行。

包你學會 AWK_正則表達式_43

exit用於退出腳本,參數為退出的狀態碼,可以通過shell中的$?獲取

函數

內建函數

AWK提供了很多方便的內建函數供編程人員使用。由於函數比較多,個人覺得單純看每個函數的使用也沒有什麼實際意義,比較容易遺忘,因此,這裏只簡單的列出常用的一些函數,只需要對其有個印象即可,使用的時候再去 查手冊 效果會更好一些吧。

數學函數

  • atan2(y, x)
  • cos(expr)
  • exp(expr)
  • int(expr)
  • log(expr)
  • rand
  • sin(expr)
  • sqrt(expr)
  • srand([expr])

字符串函數

  • asort(arr [, d [, how] ])
  • asorti(arr [, d [, how] ])
  • gsub(regex, sub, string)
  • index(str, sub)
  • length(str)
  • match(str, regex)
  • split(str, arr, regex)
  • sprintf(format, expr-list)
  • strtonum(str)
  • sub(regex, sub, string)
  • substr(str, start, l)
  • tolower(str)
  • toupper(str)

時間函數

  • systime
  • mktime(datespec)
  • strftime([format [, timestamp[, utc-flag]]])

字節操作函數

  • and
  • compl
  • lshift
  • rshift
  • or
  • xor

其它

包你學會 AWK_操作符_44

包你學會 AWK_操作符_45

包你學會 AWK_正則表達式_46

包你學會 AWK_正則表達式_47

包你學會 AWK_字段_48

用户自定義函數

函數是程序基本的組成部分,AWK允許我們自己創建自定義的函數。一個大型的程序可以被劃分為多個函數,每個函數之間可以獨立的開發和測試,提供可重用的代碼。

下面是用户自定義函數的基本語法

包你學會 AWK_正則表達式_49

輸出重定向

重定向操作符

到目前為止,我們所有的程序都是直接顯示數據到了標準輸出流,其實,我們也可以將輸出重定向到文件。重定向操作符跟在print和printf函數的後面,與shell中的用法基本一致。

包你學會 AWK_操作符_50

管道

除了將輸出重定向到文件之外,我們還可以將輸出重定向到其它程序,與shell中一樣,我們可以使用管道操作符|。

包你學會 AWK_正則表達式_51

第一次I/O操作使用了|&操作符,gawk會創建一個到運行其它程序的子進程的雙向管道,print的輸出被寫入到了subprogram的標準輸入,而這個subprogram的標準輸出在gawk中使用getline函數進行讀取。

注意:目前協同進程的標準錯誤輸出將會和gawk的標準錯誤輸出混雜在一起,無法單獨獲取標準錯誤輸出。另外,I/O緩衝可能存在問題,gawk程序會自動的刷新所有輸出到下游的協同進程的管道。但是,如果協同進程沒有刷新其標準輸出的話,gawk將可能會在使用getline函數從協同進程讀取輸出的時候掛起,這就可能引起死鎖。

我們可以使用close函數關閉雙向管道的to或者from一端,這兩個字符串值告訴gawk發送數據到協同進程完成時或者從協同進程讀取完畢時關閉管道。在使用系統命令sort的時候是這樣做是非常必要的,因為它必須等所有輸出都讀取完畢時才能進行排序。

包你學會 AWK_字段_52

上例看起來有些複雜,我們逐行分析一下

  • 首先,第一行 cmd = “tr [a-z] [A-Z]” 是在AWK中要建立雙向連接的命令
  • 第二行的print命令用於為tr命令提供輸入,而 |& 用於指出要建立雙向連接
  • 第三行用於在上面的語句close(cmd, “to”),在執行完成後關閉其to進程
  • 第四行 cmd |& getline out使用getline函數存儲輸出到變量out中
  • 最後一行使用close函數關閉命令

美化輸出

到目前為止,我們已經使用過print和printf函數顯示數據到標準輸出,但是printf函數實際上要比我們之前使用的情況更加強大得多。該函數是從C語言中借鑑來的,在處理格式化的輸出時非常有用。

包你學會 AWK_字段_53

格式化輸出標識有 %c, %d,%s 等,基本與C語言一致,這裏就不多贅述了。

執行shell命令

在AWK中執行shell命令有兩種方式

  • 使用system函數
  • 使用管道

使用system函數

system函數用於執行操作系統命令並且返回命令的退出碼到awk。

包你學會 AWK_操作符_54

使用管道

如果要執行的命令很多,可以將輸出的命令直接用管道傳遞給“/bin/sh”執行

包你學會 AWK_正則表達式_55