第一次看見awk這三個字母,是在某實習童鞋的QQ狀態里面,贊同事寫的awk如何高效。
昨天在書暢草草看了下IBM官方技術博里的文章,覺得很贊,很激動,但是有點看不太懂,還是自己抱着教科書,老老實實從頭看起吧。
哦,這里說一下,最近看的shell相關的東西,都是跟着《A Pratical Guide to Linux Commands,Editors, and Shell Programming》這本書的。
gawk是awk的GNU版本。
> 使用條件
主要是用gawk來處理結構化的數據。
什么是結構化呢,我的理解,就是一個文件里面的內容,可以按照某種記錄記錄格式,一條記錄一條記錄地讀,同時,每一條記錄中間用一個分隔符分成了若干個部分,而且所有記錄用同一個分隔符,且所有行用分隔符分出來的部分的數目相同。
當然,gawk可以結構化數據,也可以輸出結構化數據。
另外,要提的一點是,gawk在處理數據的時候,不修改原有的數據文件,只是將數據讀出來,做處理。
> 語法
gawk [option] [program] [file-list]
gawk [options] –f program-file [file-list]
白話說來,運行gawk有兩種方式:
1、直接在shell里面運行。option是運行的選項;program是gawk的代碼,要用' ' 括起來,目的是屏蔽shell對里面內容的解析;file-list是用到的數據文件。[option 有哪些?file-list多個文件怎么處理?]
2、將gawk作為獨立的文件,調用gawk文件來執行。要用-f來標記是使用的gawk文件,同時program-list是gawk文件的列表。
> 選項(option)
僅列出要緊的幾個
-F fs | 將fs作為輸入字段分隔符 |
-f program-file | 從program-file 命名文件而不是標准輸入讀取gawk程序,用戶可以多次指定這個選項 |
-v var=value | 將value賦值給var。賦值動作在gawk程序執行之前進行,並用於BEGIN模式。可以在命令行上多次指定這一選項。 |
> 組成單元
好吧,這是我根據自己理解用的一個名詞。為什么這么說呢?想想以前的各種語言,C,基本組成單元是函數,除了頭文件之外,程序是有各個函數構成的;C++類似;Java,各種方法,所以說,他們的組成單元是 函數 或 方法。
但是gawk呢?如下:
pattern{ action }
每個action用 "{","}"包圍起來,同時,"{"要跟在pattern的同一行。
pattern有多種,action各式各樣,但是整個gawk的程序,可以按照這樣的組成單元,分解開來。
所以說,寫gawk程序,就是在源文件里面,構建一個個這樣的組成單元。
gawk的組成單元,有兩個部分,pattern,action。pattern作為一個模式,決定了action在什么情況下運行,而action,則是當記錄符合條件(pattern)時,執行什么樣的動作。這里想用一個圖來說明下:
圖1 gawk處理結構化數據示意圖
如圖1所示,gawk的程度,就像是在結構數據上的套子,順着結構數據,從頭讀到尾,然后根據數據是否滿足要求,來決定執行action。這里有一個問題,在gawk文件內,多個組成單元,是怎么樣的一個執行順序,在后面會用實例檢驗一下。
根據pattern的不同,可以講組成單元分成3類:
1、系統默認
有兩個BEGIN,END。
BEGIN在所有代碼開始前執行,也就是說,他不參與pattern的判定。主要是用來初始化一些系統變量等。
END在前面的所有代碼執行完畢后執行,也沒有pattern的判定。主要是清理一些中間生成的臨時數據等等。
2、無pattern
也就是pattern為空,表示所有的結構化數據,每行數據,都要執行這個pattern下的action
3、定義pattern
實際上“定義pattern”和“無pattern”一樣,action都是用戶來寫的,這里之所以區分開,如前面所說,是基於pattern來分的。
這一類,pattern可以是一個條件判斷,也可以是一個正則表達式匹配。
> 系統變量
這些變量被gawk占用了,用的時候,直接拿來就是對應的含義:
表1 變量
$0 | 當前記錄(作為單個變量) |
$1-$n | 當前記錄中的字段 |
FILENAME | 當前輸入文件名 |
FS | (Field Split)輸入字段字段分隔符 |
NF | (Number of Fields)當前記錄的字段數目 |
NR | (Number of current Record)當前記錄的記錄編號 |
OFS | (Output Field Split)輸出字段分隔符(默認為空格) |
ORS | (Output Record Split)輸出記錄分隔符(默認為換行符) |
RS | (Record Split)輸入記錄分隔符(默認為換行) |
FS,RS,可以在BEGIN中,根據具體輸入文件的不同,進行設置。
OFS,ORS,也可以再BEGIN中,根據輸出文件的要求,進行設置。
在程序中,可以通過NF,知道一條記錄中有多少個字段,也就決定了$1-$n的n是多少。
在任何時刻都可以修改分隔符的值,具體方法是通過在程序中或者在命令行中使用--assign(-v)選項,將一項新的值指派給對應的變量。
(也就意味着,在輸入和輸出文件中,都可以出現分隔符不同一的情況)
> 函數
gawk的內置函數,用來操作數字和字符串
表2 函數
length(str) | 返回字符串str的字符個數,如果沒有參數str,返回當前記錄的字符個數 |
int(num) | 返回num的整數部分 |
index(str1,str2) | 返回str2在str1中的位置,如果str2不存在返回0 |
split(str,arr,del) | 用del做分隔符,將str的元素放置到arr[1]到arr[n]中,返回數組元素個數 |
sprintf(fmt,args) | 根據fmt格式化args並返回格式化后的字符串 |
substr(str,pos,len) | 返回str中從pos開始長度為len個字符的字符串 |
tolower(str) | str字母全部轉為小寫 |
toupper(str) | str字母全部轉為大寫 |
> 注釋
該行以 # 開頭。
> 關聯數組
我理解它類似於C#的Dictionary<k,v>,可以用字符串做索引:array[string]=value
在使用的時候,利用for可以實現:for (elem in array) print array[elem]
> 控制結構
if-else,while,for 與C語言的對應項基本一致,完全不同於Shell下的對應項,不再贅述。
初始gawk,就到這里,用一個例子來結束:
數據文件cars記錄了一組關於汽車的銷售信息(品牌,型號,生產年代,千里里程數,價格),用gawk,寫程序,統計下平均使用年限,所有車的平均價格和新車(晚於2000年)的平均價格
圖2 數據文件cars
圖3 gawk代碼
圖4 運行結果