格式化文本數據抽取工具awk

在管理和維護Linux系統過程當中,有時可能須要從一個具備必定格式的文本(格式化文本)中抽取數據,這時可使用awk編輯器來完成這項任務。發明這個工具的做者是Aho、Weinberg和Kernighan,取三我的名的首字母而得名awk。正則表達式

與sed相比,awk更擅長處理格式化文本。格式化文本通常使用某個特定的字符(稱爲域分隔符)將文本中不一樣的字段(稱爲域)隔開。例如用於保存用戶信息的系統用戶文件/etc/passwd,該文件使用冒號分別將用戶名、密碼、UID等字段分隔開。shell

1、awk命令基本格式

  • 命令格式:

awk [-F] ‘command’ input-file數組

awk –f script input-filebash

與sed相似,awk也有兩種調用方式:第一種是直接使用awk命令調用,選項F用於指定域分隔符。默認狀況下awk使用的域分隔符是空格,若是要處理的文件input-file的域分隔符不是空格,應該使用F選項另行指定。第二種方法跟sed同樣,先將要輸入的選項模式和動做放入一個腳本文件中,而後使用選項f調用。服務器

  • 命令處理過程:

awk被調用後,首先讀入第一行文本並按選項F指定的域分隔符將各個字段劃開。以/etc/passwd其中一行爲例:網絡

root:x:0:0:root:/root:/bin/bash

處理這個文件時,應該使用選項F指定域分隔符爲冒號「:」,劃分完成後將這一行稱爲一條記錄。一條記錄中的各個字段按順序稱爲域1,域2,域3……爲方便對這些字段進行處理,使用標識符「$1」表示第一個字段,「$2」表示第二個字段…依次類推。若是要表示整條記錄,應該使用標識符「$0」。編輯器

劃分記錄後,awk會按預約的模式和動做處理記錄。處理完一條記錄後,又會讀取文本第二行重複上述動做。函數

  • 模式和動做:

一個完整的awk命令由編輯語句和格式化文本組成。編輯語句由一個或多個模式和動做組成,格式化文本即用戶須要處理的文本,能夠來自文件,也能夠來自命令輸出。工具

與sed同樣,模式用來指定動做執行的文本位置。在awk中,模式能夠是條件語句、模式匹配、正則表達式等。若是沒有指定模式,awk會對全部記錄執行編輯語句。測試

動做通常放在模式後面的大括號{}內,通常是awk的內置函數,例如輸出函數print等。

下面是一個輸出文件/etc/passwd中全部用戶名的示例:

[root@localhost test]# awk -F: '{print "username:" $1}' /etc/passwd
username:root
username:bin
username:daemon
username:adm
username:lp
...

上面這個示例命令使用選項F指定域分隔符爲冒號,後面的命令中並無指定模式,awk將默認匹配全部行。放在大括號內的動做使用了awk的內置函數print,先輸出了一個字符串「username:」,而後使用標識符「$1」輸出第一個字段。

  • 文本頭、尾表達式:

在awk中,有兩個特殊的表達式BEGIN和END。BEGIN語句將在編輯語句開始執行以前運行,一般用來輸出文本頭信息。END語句則在全部編輯語句完成以後執行,通常用來輸出結束信息和統計數據等。

使用示例文件students(見查找文本工具grep),製做一個輸出學生學號和姓名,並輸出文本頭、文本尾的例子:

[root@localhost test]# awk 'BEGIN{print "Student ID     name\n-----------------------"}{print $1"\t"$2}END{print "------------END-----------"}' students
Student ID      name
-----------------------
2821020225      Liulu
2821020115      Liumin
2721020321      Xuli
2921020632      Xiayu
2721010409      Liwei
2921050313      Heli
2721030227      Wangtao
------------END-----------

2、正則表達式、元字符、運算符和關係運算符

在awk中,除了可使用正則表達式和元字符外,awk還內置了算術運算符、操做符。

  • 常見的算術運算符:

=(賦值)、+(加)、-(減)、-(負)、*(乘)、/(除)、前置++、後置++、前置--、後置--、?:(條件)、%(求模)、+=(複合加)、-=(複合減)、*=(複合乘)、/=(複合除)、%=(複合求模)、^(冪)、^=(複合冪)

  • 常見的關係運算符和操做符:

>(大於)、>=(大於等於)、<(小於)、<=(小於等於)、!=(不等於)、==(等於)

~/pattern/(匹配pattern)、!~/pattern/(不匹配pattern)

  • 邏輯運算符:

||(或)、&&(與)、!(非)

用法示例:

(1)結合正則表達式,輸出全部2007年入學的學生信息:

[root@localhost test]# awk '/27210/{print $0}' students
2721020321 Xuli         Jiangsu Luolei  12/25/92        76 81 85 79 321 80
2721010409 Liwei        Sichuan tangwei 11/21/92        98 88 85 85 356 89
2721030227 Wangtao      Yunnan  Huli    03/21/93        87 76 69 88 320 80

(2)結合正則表達式,輸出全部來自Sichuan的學生學號、姓名和總分:

[root@localhost test]# awk '/Sichuan/{print $1"\t"$2"\t"$10}' students
2821020225      Liulu   325
2721010409      Liwei   356

(3)能夠指定某個域的取值精確匹配。例如要輸出姓名爲Liwei的學生信息:

[root@localhost test]# awk '$2=="Liwei"{print $0}' students
2721010409 Liwei        Sichuan tangwei 11/21/92        98 88 85 85 356 89

(4)精確匹配時,可使用「!=」表示不匹配。例如要輸出不是來自Sichuan的學生信息:

[root@localhost test]# awk '$3!="Sichuan"{print $0}' students
2821020115 Liumin       Henan   lixia   05/14/94        78 65 59 78 280 70
2721020321 Xuli         Jiangsu Luolei  12/25/92        76 81 85 79 321 80
2921020632 Xiayu        Shanxi  Hetao   03/26/93        78 86 92 78 334 84
2921050313 Heli         Xizang  Tangwei 07/12/94        56 78 80 45 259 65
2721030227 Wangtao      Yunnan  Huli    03/21/93        87 76 69 88 320 80

(5)也可使用操做符表示匹配,例如要輸出全部輔導員不是Tangwei的學生的信息:

[root@localhost test]# awk '$4 !~/[Tt]angwei/{print $0}' students
2821020225 Liulu        Sichuan Lixia   01/23/93        89 76 88 72 325 81
2821020115 Liumin       Henan   lixia   05/14/94        78 65 59 78 280 70
2721020321 Xuli         Jiangsu Luolei  12/25/92        76 81 85 79 321 80
2921020632 Xiayu        Shanxi  Hetao   03/26/93        78 86 92 78 334 84
2721030227 Wangtao      Yunnan  Huli    03/21/93        87 76 69 88 320 80

(6)也能夠將匹配操做符與正則表達式一塊兒使用。例如輸出全部93年之後出生的學生信息:

[root@localhost test]# awk '$5 ~/^.......[3-9]/{print $0}' students
2821020225 Liulu        Sichuan Lixia   01/23/93        89 76 88 72 325 81
2821020115 Liumin       Henan   lixia   05/14/94        78 65 59 78 280 70
2921020632 Xiayu        Shanxi  Hetao   03/26/93        78 86 92 78 334 84
2921050313 Heli         Xizang  Tangwei 07/12/94        56 78 80 45 259 65
2721030227 Wangtao      Yunnan  Huli    03/21/93        87 76 69 88 320 80

(7)使用豎線「|」匹配兩邊模式之一,例如要輸出全部來自Sichuan和Yunnan的學生信息:

[root@localhost test]# awk '$3 ~/(Sichuan|Yunnan)/{print $0}' students
2821020225 Liulu        Sichuan Lixia   01/23/93        89 76 88 72 325 81
2721010409 Liwei        Sichuan tangwei 11/21/92        98 88 85 85 356 89
2721030227 Wangtao      Yunnan  Huli    03/21/93        87 76 69 88 320 80

(8)在使用$0標識符輸出全部域時,也能夠省略後面的輸出動做。下面這條命令和上面的命令效果同樣:

[root@localhost test]# awk '$3 ~/(Sichuan|Yunnan)/' students
2821020225 Liulu        Sichuan Lixia   01/23/93        89 76 88 72 325 81
2721010409 Liwei        Sichuan tangwei 11/21/92        98 88 85 85 356 89
2721030227 Wangtao      Yunnan  Huli    03/21/93        87 76 69 88 320 80

(9)可使用算術運算符對域進行計算,這在製做一些統計信息時很是有用。例如要計算全部公共課的平均成績並輸出:

[root@localhost test]# awk '{print $1"\t"$2"\t"$6"\t"$7"\t"$8"\t"$6+$7+$8"\t"($6+$7+$8)/3}' students
2821020225      Liulu   89      76      88      253     84.3333
2821020115      Liumin  78      65      59      202     67.3333
2721020321      Xuli    76      81      85      242     80.6667
2921020632      Xiayu   78      86      92      256     85.3333
2721010409      Liwei   98      88      85      271     90.3333
2921050313      Heli    56      78      80      214     71.3333
2721030227      Wangtao 87      76      69      232     77.3333

(10)能夠結合使用關係運算符和邏輯運算符,以實現更復雜的運算。例如要輸出全部來自Sichuan而且平均分大於80的學生的信息:

[root@localhost test]# awk '($3=="Sichuan" && $11>80){print $0}' students
2821020225 Liulu        Sichuan Lixia   01/23/93        89 76 88 72 325 81
2721010409 Liwei        Sichuan tangwei 11/21/92        98 88 85 85 356 89

注意:使用awk時,必定要將多個模式和條件放在括號中,將執行編輯的語句放在單引號內,函數和流控制語句放入大括號內,避免產生錯誤。

3、在awk命令中使用變量

awk中的變量有兩種:一種是內置變量,這些變量經常使用於控制輸出和保存「awk」當前工做狀態等信息,在引用變量時一般不須要使用美圓符號$;另外一種是用戶自定義變量,這些變量由用戶本身定義並使用,一般自定義變量放在BEGIN語句中初始化(賦值)。

使用自定義變量時,若是awk不能肯定自定義變量的類型,一般將其默認爲字符串進行處理。

  • 內置變量:

FILENAME:用於保存輸入文件的文件名稱。

NF:用於保存當前正在處理的記錄的域個數。

NR:用於保存從文本中讀取記錄的個數。

FNR:用於保存當前讀取的記錄數。當輸入的文件有多個時,讀取新文件時,awk會重置這個變量。

OFS:用於設置輸出分隔字段的字符,默認爲空格。

FS:用於設置字段分隔符。

ORS:用於設置輸出記錄分隔符,默認爲新的一行。

RS:用於設置記錄分隔符,默認爲新行。

OFMT:數字的輸出格式。

ENVIRON:讀取環境變量。

(1)有時可能須要對輸出的文本從新格式化(設置你想要的輸出形式),這時可使用內置變量OFS和ORS,設置輸出文本的域分隔符和記錄分隔符。

[root@localhost test]# awk 'BEGIN{FS=":"; OFS="\t"; print "NF\tNR\tusername\tshell"}{print NF,NR,$1,$7}END{print FILENAME}' /etc/passwd
NF      NR      username        shell
7       1       root    /bin/bash
7       2       bin     /sbin/nologin
7       3       daemon  /sbin/nologin
7       4       adm     /sbin/nologin
7       5       lp      /sbin/nologin
7       6       sync    /bin/sync
......

(2)有時會遇到一些使用特殊分隔符的文本。例以下面這個示例中,須要在命令中把FS和RS從新設置成文本中使用的的特殊分隔符(而不是默認的空格和新行)才能順利讀取文本:

[root@localhost test]# cat students3
2821020225#Liulu#0#A#B#0\

2821020115#Liumi#B#C#0#0\

2721020321#Xuli#0#D#A#0\

2921020632#Xiayu#A#C#0#0\

2721010409#Liwei#B#C#0#D\

2921050313#Heli#B#0#D#0\

2721030227#Wangtao#C#0#D#0\
[root@localhost test]# awk 'BEGIN{FS="#"; RS="\\n\n"}{print $1,$2}' students3
2821020225 Liulu
2821020115 Liumi
2721020321 Xuli
2921020632 Xiayu
2721010409 Liwei
2921050313 Heli
2721030227 Wangtao

(3)當輸出的字符爲一個浮點數時,可能須要指定浮點數的輸出格式,此時可使用內置變量OFMT。例如:

[root@localhost test]# awk 'BEGIN{OFMT="%.2f"; print 19.243564}'
19.24

提示:使用awk命令時,若是沒有編輯語句,只有BEGIN語句,能夠沒必要輸入文件名直接運行。

(4)有時可能在處理過程當中須要引用環境變量,此時可使用ENVIRON變量。以下是一個使用ENVIRON讀取環境變量的示例:

[root@localhost test]# awk 'END{print ENVIRON["LANG"]}' students
en_US.UTF-8

上面這個例子中雖然沒有引用文件中的內容,但使用了END表達式,所以必須使用文件參數,不然這條命令將變的不完整

  • 自定義變量

自定義變量一般用於統計並計算某個數字字段的結果,也可用於引用字符串。

(1)下面是一個統計當前文件夾中全部文件佔用空間的示例:

[root@localhost test]# ls -l | awk 'BEGIN{A=0}{A=A+$5}END{print "The size of all files of the current directory is:"A}'
The size of all files of the current directory is:13232

在上面這個示例中,首先在BEGIN表達式中定義了一個 變量A並初始化爲0.而後與ls命令輸出的第5個字段相加,獲得全部文件佔用的空間,最後在END表達式中將結果輸出。

(2)變量也能夠在動做語句以後添加,例如:

[root@localhost test]# awk '$2==NAME1{print $0}' NAME1="Liwei" students
2721010409 Liwei        Sichuan tangwei 11/21/92        98 88 85 85 356 89

提示:awk中引用變量時,一般不須要使用引用符號。但爲了便於理解和閱讀,應將變量名稱大寫並加上引用符號。

4、在awk命令中使用流程控制

在awk命令中,還內置了一些流程控制語句(簡稱流控制語句)。使用這些流控制語句,能夠完成更多複雜的抽取任務。

  • awk中的流控制語句語法結構:

awk命令中常見的流控制語句有:if、while、do-while和for語句,這些語句的基本語法結構與c語言中的語句相似。

(1)if語句的基本格式:

if (條件表達式)
{
    語句塊1
}
else
{
    語句塊2
}

(2)while語句基本格式:

while (條件表達式)
{
    語句塊
}

(3)do-while語句基本格式:

do
{
    語句塊
} while (條件表達式)

(4)for語句基本格式:

for (初始表達式;條件表達式;步長)
{
    語句塊
}

(5)控制語句:

continue:用於在循環語句中控制循環走向。執行到此語句時,當即從當前執行位置跳轉到循環開始處開始下一次循環。此語句用於標識後面的語句已經沒有必要執行了,必須當即開始下一次循環執行過程。

break:用於跳出循環。當執行到此語句時,循環當即中止,而後執行循環後面的語句。

next:使awk讀取文本的下一行。該語句告訴awk,當前行已經沒有任何價值,須要當即讀取文本的下一行。

exit:若是這條語句沒有出如今END表達式中,則當即跳轉去執行END中的語句(即直接跳過文件最後一行,執行END表達式中的語句)。若是出如今END表達式中,則awk命令當即中止執行並退出。

提示:因爲循環語句會致使命令變得很長,且影響閱讀,所以一般建議將循環語句放入腳本文件中執行。

流控制語句用法示例:

(1)例如要輸出文件students中,全部總成績320以上的學生信息:

[root@localhost test]# awk 'BEGIN{OFS="\t"}{if($10>=320) print $1,$2,$10}' students
2821020225      Liulu   325
2721020321      Xuli    321
2921020632      Xiayu   334
2721010409      Liwei   356
2721030227      Wangtao 320

(2)輸出來自Sichuan的學生中,總成績大於等於320的學生信息:

[root@localhost test]# awk 'BEGIN{OFS="\t"}{if($3=="Sichuan" && $10>=320) print $1,$2,$10}' students
2821020225      Liulu   325
2721010409      Liwei   356

(3)計算ls命令傳送過來的信息中,全部普通文件的大小和文件夾的個數:

 

[root@localhost test]# cat total.awk
BEGIN{
        #Initialize A and count.
        A=0;
        count=0;
}
{
        #if the first character of field 1 is '-', then add A and the  value of field 5.
        if($1 ~/^-/)
        A+=$5;
        #if the first character of field 1 is 'd', then count plus 1.
        if($1 ~/^d/)
        count++;
}
END{
        print "total:"A;
        #count-2:because there are current directory and up level directory.
        print count-2," directories.";
}
[root@localhost test]# ls -al /etc | awk -f total.awk
total:1423743
84  directories.

(4)Linux系統管理員常常須要關注網絡延時大小,以避免對應用服務的正常運做形成影響。最好的解決辦法是每隔一段時間,使用ping命令測試與某個服務器的鏈接,並統計延時。

下面是一個使用ping命令發送4個ICMP數據包測試雙向連通性,並用awk從中計算最大延時、最小延時和平均延時的示例:

[root@localhost test]# cat ping.awk
BEGIN{
        #使用從新定義變量FS的方法設置域分隔符爲冒號、等號及空格
        FS="[:= ]";
        AVG=0;
        MAX=0;
        MIN=0;
}
{
        for(I=1;I<9;I++)
        {
               #若是當前爲第二條記錄,則爲變量MAX、MIN、IP_ADDR賦值
                if(NR==2)
                {
                        MAX=$11;
                        MIN=$11;
                        IP_ADDR=$4;
                }
                if(NR>1 && NR<6)
                {
           #計算延時總和
                        AVG+=$11;
                        if($11>MAX)
                                MAX=$11;
                        if($11<MIN)
                                MIN=$11;
                }
    #若是記錄數大於6,則跳出循環並執行END中的表達式
                if(NR>=6)
                        exit;
    #不然,讀取系一條記錄,從新開始循環
                next;
        }
}
END{
        AVG=AVG/4;
        print "IP address:",IP_ADDR;
        print "Avg:",AVG,"ms";
        print "Max:",MAX,"ms";
        print "Min:",MIN,"ms";
}

爲了方便抽取數據,先在BEGIN語句中定義了3中域分隔符:冒號、等號和空格。

因爲ping命令返回的第1行是由ping自動生成的簡要提示信息,所以awk將忽略第1行。

第2個到第5個記錄返回了TTL值和延時等信息,後面則是一些統計信息,所以awk處理過程當中只處理第2個到第5個記錄。

5、awk命令中的函數

awk命令中的函數分爲兩種:內置函數和用戶自定義函數。內置函數可能是一些用來處理字符串和數學運算的函數,用戶自定義函數則是由用戶本身定義的功能性函數。

  • 內置函數:

awk命令中函數的用法與c語言中函數的用法十分類似。常見的內置函數以下:

sub(regexp, replacement [, target]):當模式regexp第1次出現時,用replacement替換。

gsub(regexp, repalcement [, target]):將與模式regexp匹配的內容所有用replacement替換。

index(string, find):返回與find匹配的字符串第1次在string中出現的位置。

length([string]):返回字符串string的長度。

match(string, regexp [, array]):返回與regexp字符串第1次匹配的位置,若是不存在則返回0.

split(string, array [, fieldsep]):將string按照字段分隔符filedsep進行分割,分割後的字段存入數組array,返回字符串數組元素個數。

printf(format, expr1 [, expr2, …]):格式化輸出函數。

substr(string, start, [, length]):從string中返回從start開始,長度爲length的子字符串。

tolower(string):將string中的大寫字符轉換成小寫。

toupper(string):將string中的小寫字符轉換成大寫。

print:輸出函數。

rand():返回一個0到1之間的隨機數。

system():將傳遞來的字符串按shell命令執行。

atan2(x, y):反正切函數。

cos(x):餘弦函數。

sin(x):正弦函數。

log(x):x的天然對數值。

exp(x):E的x次冪。

int(x):將x轉換爲整數類型。

sqrt(x):x的平方根。

用法示例:(示例文件students2.2內容以下)

[root@localhost test]# cat students2.2
2821020225      Liulu           0       A       B       0
2821020115      Liumin          B       C       0       0
2721020321      Xuli            0       0       B       D
2921020632      Xiayu           0       0       0       0
2721010409      Liwei           C       0       0       0
2921050313      Heli            A       0       A       B
2721030227      Wangtao         B       B       C       0

(1)使用sub函數能夠查找第1次出現的模式並替換。例如用sub函數修改匹配模式的記錄:

[root@localhost test]# awk '$1 ~/28210202/{print sub(/Liulu/,"Lilu",$0)"\n"$0}' students2.2
1
2821020225      Lilu            0       A       B       0

在上面的命令執行後,在第1行返回了修改的次數(對應sub函數),第2行輸出了修改後的記錄(對應$0)。

(2)利用函數gsub將全部匹配到的大寫字母A替換成數字95,而後返回一共修改的次數:

[root@localhost test]# awk 'BEGIN{N=0}{N+=gsub(/A/,"95",$0); print $0}END{print N}' students2.2
2821020225      Liulu           0       95      B       0
2821020115      Liumin          B       C       0       0
2721020321      Xuli            0       0       B       D
2921020632      Xiayu           0       0       0       0
2721010409      Liwei           C       0       0       0
2921050313      Heli            95      0       95      B
2721030227      Wangtao         B       B       C       0
3

因爲函數gsub執行一個記錄的替換就會返回一次,所以此處用N+=gsub()。不然只會返回最後一條記錄替換的次數。

(3)利用index函數能夠返回第1次匹配字符串的位置:

[root@localhost test]# awk '{print index($0, "L"); if(NR==2) exit}' students2.2
12
12

(4)利用length函數查看字符串長度:

[root@localhost test]# awk '{print length($0); if(NR==2) exit}' students2.2
25
26

(5)使用match函數查看匹配模式首次出現的位置:

[root@localhost test]# awk 'BEGIN{print match("Hello! Welcome to Beijing!", "B")}'
19

(6)利用split函數將字符串按指定的分隔符拆開,而後放入指定的數組中並返回數組的元素個數:

[root@localhost test]# awk 'BEGIN{print split("FV7H8-42D17-08D0Q-8QAG9-YPUZA",ARR,"-");for(I in ARR)print ARR[I]}'
5
8QAG9
YPUZA
FV7H8
42D17
08D0Q

另外一種方式:

[root@localhost test]# awk 'BEGIN{AN=split("FV7H8-42D17-08D0Q-8QAG9-YPUZA",ARR,"-");print AN;for(I=1;I<=AN;I++)print ARR[I]}'
5
FV7H8
42D17
08D0Q
8QAG9
YPUZA

注意:數組下標從1開始。

提示:在awk中使用數組時,能夠不用先定義,也沒必要指出元素的個數。

(7)利用tolower函數將字符串中的大寫字符轉換成小寫:

[root@localhost test]# awk '$1=="2821020225"{print tolower($0)}' students2.2
2821020225      liulu           0       a       b       0

(8)利用toupper函數將字符串中的小寫字符轉換成大寫:

[root@localhost test]# awk '$1=="2821020225"{print toupper($0)}' students2.2
2821020225      LIULU           0       A       B       0

(9)利用rand函數返回一個0到1之間的隨機數:

[root@localhost test]# awk 'BEGIN{print rand()}'
0.237788

(10)sin、cos函數能夠計算正弦、餘弦:

[root@localhost test]# awk 'BEGIN{PI=3.1415926;print sin(PI/4),cos(PI/4)}'
0.707107 0.707107

(11)使用log函數能夠返回天然對數:

[root@localhost test]# awk 'BEGIN{print log(100)}'
4.60517

(12)exp函數能夠返回e的x次冪:

[root@localhost test]# awk 'BEGIN{print exp(256)}'
1.51143e+111

(13)sqrt函數能夠計算平方根:

[root@localhost test]# awk 'BEGIN{print sqrt(256)}'
16

(14)格式化輸出函數printf(與c函數裏的printf同樣),可使用修飾符對輸出的內容進行格式控制,其基本語法以下:

print format, [argument]…

printf經常使用的修飾符:

%c:做爲一個ASCII字符輸出。

%s:做爲一個字符串輸出。

%o:做爲一個八進制數輸出。

%x:做爲一個十六進制輸出。

%d:做爲一個整數輸出。

%e:做爲一個科學型浮點數輸出。

%f:做爲一個浮點型數字輸出。

%g:由awk決定浮點數輸出的形式。

除了以上一些修飾符外,awk也容許像c語言那樣使用「-」、域寬和「.」進一步細化輸出格式。

使用printf函數將數字100分別轉換成字符、八進制、十六進制數:

[root@localhost test]# awk 'BEGIN{A=100;printf "%c\n%o\n%x\n",A,A,A}'
d
144
64

(15)使用printf的修飾符能夠將生成的數據轉換成相應的格式:

[root@localhost test]# awk 'BEGIN{printf "%e\n%f\n%g\n",exp(30),exp(30),exp(30)}'
1.068647e+13
10686474581524.462891
1.06865e+13

(16)有時在計算一個數字時要保留必定的精度,這時可使用「.」指定精度,例如:

[root@localhost test]# awk 'BEGIN{printf "%4.5f\n",sqrt(47)}'
6.85565

(17)從/etc/passwd中抽取用戶名和用戶使用的shell,使用%s對齊輸出:

[root@localhost test]# awk 'BEGIN{FS=":";printf "%-15s  %-15s\n","username","shell";printf "------------------------------\n"}{printf "%-15s       %-15s\n",$1,$7}' /etc/passwd
username        shell          
------------------------------
root            /bin/bash      
bin             /sbin/nologin  
daemon          /sbin/nologin  
adm             /sbin/nologin  
lp              /sbin/nologin  
...

(18)、爲了使輸出的段對齊,這裏使用修飾符,例如:

[root@localhost test]# awk '{printf "%-10s\t%-10s\t%d\t%d\t%d\t%d\t%d\n",$1,$2,$6,$7,$8,$6+$7+$8,($6+$7+$8)/3}' students
2821020225      Liulu           89      76      88      253     84
2821020115      Liumin          78      65      59      202     67
2721020321      Xuli            76      81      85      242     80
2921020632      Xiayu           78      86      92      256     85
2721010409      Liwei           98      88      85      271     90
2921050313      Heli            56      78      80      214     71
2721030227      Wangtao         87      76      69      232     77
  • 用戶自定義函數

在awk命令中,還容許使用用戶自定義函數。與C語言中的函數做用範圍不一樣,自定義函數不管出如今命令中的什麼位置,做用都是全局的,而函數中定義的變量只做用於函數內部。若是須要從函數中返回一個值,可使用return語句。

在awk命令中自定義函數的基本格式:

function name([參數列表])
{
    語句塊;
    [return ...];
}

在調用函數時,只須要使用函數名並加上要使用的參數列表便可,無須使用特別的語句。

(1)下面是一個計算文件students中學生公共課總成績和平均成績的例子:

[root@localhost test]# cat ex.awk 
function Add(A,B,C)
{
        return A+B+C;
}
function Avg(A,B,C)
{
        return (A+B+C)/3;
}
{
        printf "%-10s\t%-10s\t%d\t%d\t%d\t%d\t%d\n",$1,$2,$6,$7,$8,Add($6,$7,$8),Avg($6,$7,$8);
}
END{
        printf "%s\n",FILENAME;
}
[root@localhost test]# awk -f ex.awk students
2821020225      Liulu           89      76      88      253     84
2821020115      Liumin          78      65      59      202     67
2721020321      Xuli            76      81      85      242     80
2921020632      Xiayu           78      86      92      256     85
2721010409      Liwei           98      88      85      271     90
2921050313      Heli            56      78      80      214     71
2721030227      Wangtao         87      76      69      232     77
students

(2)對students中的學生成績進行篩選,只輸出學號,學生姓名和課程中的最高成績並保存爲strudents_max.

爲實現上述功能,此處可使用條件三目運算符和數組兩種方法。爲此所以兩個腳本文件stu.awk1和stu.awk2:

[root@localhost test]# cat stu.awk1
function Max(Arr)
{
        Ma=Arr[1];
        for(I in Arr)
        {
                if(Ma<Arr[I])
                        Ma=Arr[I];
        }
        return Ma;
}
{
        for(J=1;J<5;J++)
                Ar[J]=$(J+2);
        printf "%-10s\t%-10s\t%d\n",$1,$2,Max(Ar);
}
[root@localhost test]# cat stu.awk2
function Max(A,B,C,D)
{
        A>B?A:A=B;
        C>D?C:C=D;
        Ma=(A>C)?A:C;
        return Ma;
}
{
        printf "%-10s\t%-10s\t%d\n",$1,$2,Max($3,$4,$5,$6);
}

使用awk的選項f調用stu.awk腳本,並將結果顯示並保存到students_max中:

[root@localhost test]# awk -f stu.awk1 students | tee students_max
2821020225      Liulu           89
2821020115      Liumin          78
2721020321      Xuli            85
2921020632      Xiayu           92
2721010409      Liwei           98
2921050313      Heli            80
2721030227      Wangtao         88
[root@localhost test]# awk -f stu.awk2 students | tee students_max2
2821020225      Liulu           89
2821020115      Liumin          78
2721020321      Xuli            85
2921020632      Xiayu           92
2721010409      Liwei           98
2921050313      Heli            80
2721030227      Wangtao         88

注意:在C語言中A>B?A:(A=B)能夠簡寫成A>B?:(A=B),但在awk中這是不容許的。

(3)最後,引用一個使用awk監視磁盤並向用戶發出警告的例子。在這個例子中,當某一個文件系統空閒不足10%時,經過system函數運行mail命令向管理員發出警告信息。

使用的腳本和執行過程以下:

[root@localhost test]# cat df.awk
function mess(A,B,C)
{
        if(B>=0.9)
        {
                system("date +'%F %r'>/root/df.tmp; df -h>>/root/df.tmp; cat /root/df.tmp | mail -s 'Disk Warning' 1025399680@qq.com,root;rm -rf /root/df.tmp");
                printf "Disk Warning!\nFile System:%s\nUsed:%3.0f\nMounted on:",A,B*100,C;
        }
}
{
        if($1 ~/^\/dev\/sd/)
        {
                N=$3/$2;
                if(N>=0.9)
                        mess($1,N,$6);
        }
}
[root@localhost test]# df | awk -f df.awk

注意:在調用函數system時,必定要將shell命令放入雙引號中,shell命令本來應放入雙引號中的參數此時應該放入單引號中。

相關文章
相關標籤/搜索