awk比較傾向于将一行分成數個“字段”來處理。是以,awk相當适合處理小型的資料處理。awk通常運作的模式是:
[email protected]:~/study/awk$ awk '條件類型 1 { 動作 1} 條件類型2 {動作2} ...' filename
awk後面接兩個單引号并加上大括号{}來設定想要對資料進行的處理動作。awk可以處理後續接的檔案,也可以讀取來自前個指令的standardoutput。但如前面說的,awk主要是處理每一行的字段内的資料,而預設的字段分隔符為空格鍵或[tab]鍵。舉例來說,我們用last可以将登陸者的資料取出來,結果如下所示:
[email protected]:~/study/awk$ last -n 5 <==僅取出前5行
root pts/1 192.168.1.100 Tue Feb 10 11:21 still logged in
若想取出帳号與登陸者的IP,且帳号與IP之間以[tab]隔開,則會變成這樣
[email protected]:~/study/awk$ last -n 5|awk '{print $1 "\t" $3}'
root 192.168.1.100
上面是awk最常使用的動作,通過print的功能将字段資料列出來!字段的分隔則以空格鍵或者[tab]按鍵來隔開。因為不論哪一行我都要處理,是以,不需要有條件類型的限制!我所想要的第一列及第三列,但是,第五行的内容很奇怪。這是因為資料格式的問題,是以使用awk的時候,請先确認一下你的資料,如果是連續性的資料,請不要有空格或[tab]在内,否則,就會像這個例子這樣,會發生誤判。
另外,由上面這個例子你也會知道,在每一行的每個字段都是有變量名稱的,那就是$1,$2等變量名稱。以上面的例子來說,root是$1,因為它是第一列嘛!至于192.168.1.100是第三列,是以它就是$3,後面以此類推。還有個變量,那就是$0,$0代表一整行資料的意思。以上面的例子來說,第一行的$0代表的就是"root.."那一行。由此可知,剛才上面五行當中,整個awk的處理流程是:
1、讀入第一行,并将第一行的資料填入$0,$1,$2等變量當中;
2、依據條件類型的限制,判斷是否需要進行後面的操作
3、做完所有的動作與條件類型
4、若還有後續的“行”的資料,則重複上面1~3的步驟,直到所有的資料都讀完為止
故 awk以行 為一次處理的機關,而以字段為最小的處理機關
awk怎麼知道我到底這個資料有幾行和幾列呢?
變量名稱 代表意義
NF 每一行($0)擁有的字段總數
NR 目前awk所處理的是“第幾行”資料
FS 目前的分隔字元,預設是空格鍵
我們繼續以上面last -n 5 的例子來做說明,如果我想要:
列出每一行的帳号(就是$1)
列出目前處理的行數(就是awk内的NR變量)
并且說明,該行有多少個字段(就是awk内的NF變量)
則可以這樣
注意:awk後續的所有動作的是以單引号" ' "擴住的
[email protected]:~/study/awk$ last -n 5|awk '{print $1 "\t lines:" NR "\t columes:" NF}'
root 192.168.1.100 lines:1 columes:10
root 192.168.1.100 lines:2 columes:11
root 192.168.1.100 lines:3 columes:9
awk邏輯運算符
> 大于
< 小于
>= 大于等于
<= 小于等于
== 等于
!= 不等于
[email protected]:~/study/awk$ cat /etc/passwd| awk ' $3<10 {print $1 "\t" $3}'
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
...
...
...
[email protected]:~/study/awk$ cat /etc/passwd| \
> awk '{FS=":"} $3<10 {print $1 "\t" $3}'
root:x:0:0:root:/root:/bin/bash
daemon 1
bin 2
sys 3
sync 4
games 5
man 6
lp 7
mail 8
news 9
第一行沒有正确顯示出來,因為我們讀取第一行的時候,那些$1,$2...預設還是以空格鍵為分隔的,是以雖然我們定義了FS=“:”,但是卻僅能在第二行後才開始生效。那怎麼辦呢?我們可以預先設定awk的變量,利用BEGIN這個關鍵字,這樣做:
[email protected]:~/study/awk$ cat /etc/passwd | awk 'BEGIN {FS=":"} $3 < 10 {print $1 "\t" $3}'
root 0
daemon 1
bin 2
sys 3
sync 4
games 5
man 6
lp 7
mail 8
news 9
而除了BEGIN之外,還有END。使用awk來進行"計算功能"
假設我有一個薪資資料表檔案名為pay.txt,内容是這樣的:
[email protected]:~/study/awk$ cat pay.txt
Name 1st 2nd 3th
VBird 23000 24000 25000
DMTsai 21000 20000 23000
Bird2 43000 42000 41000
如何能計算每個人的總額,并且格式化輸出呢?可以這樣考慮:
第一行總是說明,是以第一行不要進行加總(NR==1時處理)
第二行以後就會有加總的情況出現(NR>=2以後處理)
[email protected]:~/study/awk$ 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
上面的例子有幾個重要事項應該要說明的:
所有awk的動作,即在{}内的動作,如果有需要多個指令輔助時,可利用“;”間隔,或者直接以[Enter]按鍵來隔開每個指令,例如上面的範例中,鳥哥共按了三次[Enter]
注意:在指令中的分隔符
邏輯運算當中,如果是“等于”的情況,則務必使用兩個等号“==”!
格式化輸出時,在printf的格式設定當中,務必加上\n,才能進行分行!
與bash、shell的變量不同,在awk當中,變量可以直接使用,不需加上$符号
awk的動作内{}也是支援if(條件)的。舉例來說,上面的指令可以修改成為這樣:
[email protected]:~/study/awk$ 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}'
Name 1st 2nd 3th Total
VBird 23000 24000 25000 72000.00
DMTsai 21000 20000 23000 64000.00
Bird2 43000 42000 41000 126000.00