此文章取至鳥哥,awk對於使用Bash的部分是非常重要的一環。
http://linux.vbird.org/linux_basic/0330regularex/0330regularex-fc4.php
awk 工具簡介
相較於 sed 常常作用於一整個行的處理, awk 則比較傾向於一行當中分成數個『欄位』來處理。 因此,awk 相當的適合處理小型的數據資料處理呢!awk 通常運作的模式是這樣的:
[root@linux ~]# awk '條件類型1{動作1} 條件類型2{動作2} ...' filename
|
awk 可以處理後續接的檔案,也可以讀取來自前個指令的 standard output 。 但如前面說的, awk 主要是處理『每一行的欄位內的資料』,而預設的『欄位的分隔符號為 "空白鍵" 或 "[tab]鍵" 』!舉例來說,我們用 last 可以將登入者的資料取出來, 結果如下所示:
[root@linux ~]# last
dmtsai pts/0 192.168.1.12 Mon Aug 22 09:40 still logged in
root tty1 Mon Aug 15 11:38 - 11:39 (00:01)
reboot system boot 2.6.11 Sun Aug 14 18:18 (7+15:41)
dmtsai pts/0 192.168.1.12 Fri Aug 12 12:07 - 12:08 (00:01)
|
若我想要取出帳號與登入者的 IP ,且帳號與 IP 之間以 [tab] 隔開,則會變成這樣:
[root@linux ~]# last | awk '{print $1 "\t" $3}'
dmtsai 192.168.1.12
root Mon
reboot boot
dmtsai 192.168.1.12
|
因為不論哪一行我都要處理,因此,就不需要有 "條件類型" 的限制!我所想要的是第一欄以及第三欄, 但是,第二行及第三行的內容怪怪的~這是因為資料格式的問題啊!所以囉~使用 awk 的時候,請先確認一下您的資料當中,如果是連續性的資料,請不要有空格或 [tab] 在內,否則,就會像這個例子這樣,會發生誤判喔!
另外,由上面這個例子您也會知道,在每一行的每個欄位都是有變數名稱的,那就是 $1, $2... 等變數名稱,以上面的例子來說, dmtsai 是 $1 ,因為他是第一欄嘛!至於 192.168.1.12 是第三欄, 所以他就是 $3 啦!後面以此類推~呵呵!還有個變數喔!那就是 $0 ,$0 代表『一整列資料』的意思~ 以上面的例子來說,第一行的 $0 代表的就是『dmtsai pts/0.... 』那一行啊! 由此可知,剛剛上面四行當中,整個 awk 的處理流程是:
- 讀入第一行,並將第一行的資料填入 $0, $1, $2.... 等變數當中;
- 依據 "條件類型" 的限制,判斷是否需要進行後面的 "動作";
- 做完所有的動作與條件類型;
- 若還有後續的『行』的資料,則重複上面 1~3 的步驟,直到所有的資料都讀完為止。
經過這樣的步驟,您會曉得, awk 是『以行為一次處理的單位』, 而『以欄位為最小的處理單位』。好了,那麼 awk 怎麼知道我到底這個資料有幾行?有幾欄呢?這就需要 awk 的內建變數的幫忙啦~
變數名稱 | 代表意義 |
NF | 每一行 ($0) 擁有的欄位總數 |
NR | 目前 awk 所處理的是『第幾行』資料 |
FS | 目前的分隔字元,預設是空白鍵 |
我們繼續以上面例子來做說明,如果我想要列出每一行的帳號,並且列出目前處理的行數, 並且說明,該行有多少欄位,則可以這樣 (注意, awk 後續的所有動作以 ' 括住, 所以,內容如果想要以 print 列印時,記得,非變數的文字部分,包含上一小節 printf 提到的格式中,都需要使用雙引號來定義出來喔!)
[root@linux ~]# last | awk '{print $1 "\t lines: " NR "\t columes: " NF}'
dmtsai lines: 1 columes: 10
root lines: 2 columes: 9
reboot lines: 3 columes: 9
dmtsai lines: 4 columes: 10
|
這樣可以瞭解 NR 與 NF 的差別了吧?好了,底下來談一談所謂的 "條件類型" 了吧!
awk 的邏輯運算字元
運算單元 | 代表意義 |
> | 大於 |
< | 小於 |
>= | 大於或等於 |
<= | 小於或等於 |
== | 等於 |
!= | 不等於 |
值得注意的是那個 == 的符號,因為在『邏輯運算』上面, 就是所謂的大於、小於、等於等等的判斷式上面,我們習慣上是以 == 來表示,而如果是直接給予一個值,例如變數設定時,就直接使用 = 而已。 好了,我們實際來運用一下邏輯判斷吧!舉例來說,在 /etc/passwd 當中是以冒號 ":" 來作為欄位的分隔,那假設我要查閱,第三欄小於 10 以下的數據,並且僅列出帳號與第三欄, 那麼可以這樣做:
[root@linux ~]# cat /etc/passwd | \
> awk '{FS=":"} $3 < 10 {print $1 "\t " $3}'
root:x:0:0:root:/root:/bin/bash
bin 1
daemon 2
......(以下省略)......
|
[root@linux ~]# cat /etc/passwd | \
> awk 'BEGIN {FS=":"} $3 < 10 {print $1 "\t " $3}'
root 0
bin 1
daemon 2
......(以下省略)......
|
Name 1st 2nd 3th
VBird 23000 24000 25000
DMTsai 21000 20000 23000
Bird2 43000 42000 41000
|
[root@linux ~]# cat pay.txt | \
> awk 'NR==1{printf "%10s %10s %10s %10s %10s\n",$1,$2,$3,$4,"Total" }
NR>=2{total = $2 + $3 + $4
printf "%10s %10d %10d %10d %10.2f\n", $1, $2, $3, $4, total}'
Name 1st 2nd 3th Total
VBird 23000 24000 25000 72000.00
DMTsai 21000 20000 23000 64000.00
Bird2 43000 42000 41000 126000.00
|
- 所有的動作,亦即在 {} 內的動作,如果有需要多個指令輔助時,可利用分號『;』間隔, 或者直接以 [Enter] 按鍵來隔開每個指令,例如上面的 NR>=2 後面接的動作, 利用 total = ... 那個指令來指定加總,而後續則以 printf 來格式化輸出!
- 邏輯運算當中,如果是『等於』的情況,則務必使用兩個等號『==』!
- 格式化輸出時,在 printf 的格式設定當中,務必加上 \n ,才能進行分行!
- 與 bash shell 的變數不同,在 awk 當中,變數可以直接使用,不需加上 $ 符號。
[root@linux ~]# cat pay.txt | \
> awk '{if(NR==1) printf "%10s %10s %10s %10s %10s\n",$1,$2,$3,$4,"Total"}
NR>=2{total = $2 + $3 + $4
printf "%10s %10d %10d %10d %10.2f\n", $1, $2, $3, $4, total}'
|
除此之外, awk 還可以幫我們進行迴圈計算喔!真是相當的好用! 不過,那屬於比較進階的單獨課程了,我們這裡就不再多加介紹。如果您有興趣的話, 可以到中研院的網站查詢喔:http://phi.sinica.edu.tw/aspac/reports/94/94011/,鳥哥這裡也有一份 pdf 檔的備份:http://linux.vbird.org/linux_basic/0330regularex/awk.pdf。 您可以自行參閱一下該文章的內容,裡頭可以好好的查閱一下關於陣列與迴圈方面的介紹, 我認為該文章寫的很棒喔!該介紹的都介紹了!很好~我喜歡~ ^_^
留言列表