天天看點

鳥哥的Linux私房菜 基礎學習篇 第三版 第十二章 正規表達式與檔案格式化處理 12.4.2 awk 好用的資料處理工具

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