文本過濾工具之AWK


一、AWK簡介

   AWK三大文本處理工具之一,是一個非常強大的文本處理工具。它不僅是 Linux 中也是任何環境中現有的功能最強大的數據處理引擎之一。這種編程及數據操作語言(其名稱來自於它的創始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首個字母)的最大功能取決於一個人所擁有的知識。AWK 提供了極其強大的功能:可以進行樣式裝入、流控制、數學運算符、進程控制語句甚至於內置的變量和函數。它具備了一個完整的語言所應具有的幾乎所有精美特性。實際上AWK的確擁有自己的語言:AWK 程序設計語言,三位創建者已將它正式定義爲“樣式掃描和處理語言”。它允許您創建簡短的程序,這些程序讀取輸入文件、爲數據排序、處理數據、對輸入執行計算以及生成報表,還有很多其他的功能;在Linux系統中awk鏈接到gawk,gawk是awk的GNU版本,它提供了Bell實驗室和GNU的一些擴展,下面我們介紹的awk就是以GNU的gawk爲例來講解

二、AWK工作原理

   在Linux系統中,"/etc/passwd"是一個非常典型的格式化文件,各字段之間用":"作爲分隔符隔開,Linux系統中的大部分日誌文件也是格式化的文件,處理這些文件從中提取相關信息是管理員的日常工作之一,有了AWK工具的幫助便使得這些工作變得很輕鬆

[root@localhost ~]# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
... ... ...

若需要查找並打印出"/etc/passwd"的用戶名,用戶ID及組ID,使用下面的awk命令即可完成:

[root@localhost ~]# awk -F: '{print $1,$3,$4}' /etc/passwd

   awk從文件或者標準輸入讀取信息,與sed一樣信息的讀取也是逐行讀取的,處理過程與sed類似,不同的是awk將文本文件中的一行視爲一個記錄,而將一行中的某一部分作爲記錄中的一個字段。爲了操作這些不同的字段,awk借用了shell的方法,用"$1,$2,$3..."這樣的方式來順序地表示行(記錄)中的不同的字段。特殊地awk用"$0"表示整個行(記錄)。不同的字段之間是用稱作分隔符的字符來分隔開來的。系統默認的分隔符是空格,awk允許在命令中使用"[-F 分隔符]"的形式指定特定分隔符

在上述示例中,awk命令對"/etc/passwd"的處理過程如下圖:


三、AWK的調用方式

AWK有三種調用方式如下

1、命令行鍵入方式,也是最常用的一種方式,命令格式爲:

awk [-F 分隔符] awk指令 輸入文件
shell 命令 | awk [-F 分隔符] awk指令

   其中"[-F 分隔符]"爲可選,awk使用空格作爲缺省的分隔符,因此如果要查看有空格的文件,不用指定這個選項,但如果要查看如"/etc/passwd"就需要使用"-F"選項指定以":"爲分隔符了。

awk指令由pattern(模式)和action(動作)或是兩者的組合組成,常見的形式爲"'/pattern/{action}",awk指令必須使用單引號;pattern是正則表達式、判斷條件真假的表達式或兩者的組合,多個pattern間使用","分隔;action是awk所要採取的動作,由awk語句組成,多個awk語句使用“;”分隔

2、編寫awk運行腳本

   是將所有awk命令插入一個文件,並使用awk程序執行,然後用awk命令解釋器作爲腳本的首行,通過鍵入腳本腳本名稱來調用

3、將所有的awk命令插入一個單獨文件,然後調用:

awk -f awk-script-file 輸入文件

注: -f:指定寫有awk命令的文件名    "輸入文件":需要使用awk進行處理的文件名


用法格式

awk [options] 'script' file1 file2, ...
awk [options] 'PATTERN { action }' file1 file2, ...

options:

   -F fs|--field-separator=fs:指定文件分隔符

   -v var=val|--assign=var=val:賦值一個用戶定義變量

   -f scripfile|--file scriptfile:從腳本文件中讀取awk命令

   -W compact|--compat:在兼容模式下運行awk


四、print在AWK中的使用

使用"/etc/passwd"文件做測試:

示例: 打印出整行

[root@localhost ~]# awk '{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
... ... ...

示例:打印出"/etc/passwd"文件中以"/bash"結尾的行

[root@localhost ~]# awk '/bash/' /etc/passwd
root:x:0:0:root:/root:/bin/bash
mockbuild:x:500:500::/home/mockbuild:/bin/bash
mysql:x:498:498::/home/mysql:/bin/bash

示例:查找"/etc/passwd"文件中包含"nologin"的行,以":"爲分隔符並打印出第一個與第三個字段

[root@localhost ~]# awk -F: '/nologin/{print $1,$3}' /etc/passwd | head -3
bin 1
daemon 2
adm 3

示例:打印每一行的最後一個字段

[root@localhost ~]# awk -F: '{print $NF}' /etc/passwd
/bin/bash
/sbin/nologin
/sbin/nologin
... ... ...

示例:打印出每行的倒數第二個字段,並在後面打印ALLEN

[root@localhost ~]# awk -F: '{print $(NF-1),"ALLEN"}' /etc/passwd
/root ALLEN
/bin ALLEN
/sbin ALLEN
... ... ...

示例:打印出每一行的行號

[root@localhost ~]# awk '{print NR,$0}' /etc/passwd
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
... ... ...

示例:打印當前系統環境變量"PATH"

[root@localhost ~]# awk 'BEGIN{print ENVIRON["PATH"];}'
/usr/local/apache/bin:/usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin

示例:修改打印輸出符號,特殊字符需要轉義

[root@localhost ~]# awk -F: -v OFS=. '{print $1,$NF}' /etc/passwd | head -2
root./bin/bash
bin./sbin/nologin
[root@localhost ~]# awk -F: -v OFS=\<\> '{print $1,$NF}' /etc/passwd | head -2
root<>/bin/bash
bin<>/sbin/nologin
[root@localhost ~]# awk -F: -v OFS=- '{print $1,$NF}' /etc/passwd | head -2
root-/bin/bash
bin-/sbin/nologin

AWK變量:

AWK默認有很多變量,如前面所用的"$1-$n"、$0、OFS、NF等,如下:

變量註釋
$n當前記錄的第n個字段,字段間由FS分隔。
$0完整的輸入記錄。
ARGC命令行參數的數目。
ARGIND命令行中當前文件的位置(從0開始算)。
ARGV包含命令行參數的數組。
CONVFMT數字轉換格式(默認值爲%.6g)
ENVIRON環境變量關聯數組。
ERRNO最後一個系統錯誤的描述。
FIELDWIDTHS字段寬度列表(用空格鍵分隔)。
FILENAME當前文件名。
FNR同NR,但相對於當前文件。
FS字段分隔符(默認是任何空格)。
IGNORECASE如果爲真,則進行忽略大小寫的匹配。
NF當前記錄中的字段數。
NR當前記錄數,就是行號,從1開始
OFMT數字的輸出格式(默認值是%.6g)。
OFS輸出字段分隔符(默認值是一個空格)。
ORS輸出記錄分隔符(默認值是一個換行符)。
RLENGTH由match函數所匹配的字符串的長度。
RS記錄分隔符(默認是一個換行符)。
RSTART由match函數所匹配的字符串的第一個位置
SUBSEP數組下標分隔符(默認值是\034)


五、printf在awk中的使用

命令格式:     printf format1,format2..., item1,item2...    

printf與print的不同之處是不會自動換行,而且還需要對每個字體做格式化輸出,當然命令本身還多了個f

修飾符:
-:  左對齊
+:  顯示數值符號
N: 顯示寬度
格式化符註釋
%d%i 十進制有符號整數
$f浮點數
%s字符串
%u十進制無符號整數
%e%E 科學計數法顯示數值
%x%X 無符號以十六進制表示的整數
%g%G 以tuip科學計數法或浮點數的格式顯示數值
%c顯示字符的ASCII碼
%o無符號以八進制表示的整數
%p指針的值
%%顯示%自身

示例:左對齊與右對齊分別測試打印出"/etc/passwd"文件中的第一個字段與最後一個字段

[root@localhost ~]# awk -F: '{printf "%15s %15s\n",$1,$NF}' /etc/passwd | head -3
           root       /bin/bash
            bin   /sbin/nologin
         daemon   /sbin/nologin
[root@localhost ~]# awk -F: '{printf "%-15s %15s\n",$1,$NF}' /etc/passwd | head -3
root                  /bin/bash
bin               /sbin/nologin
daemon            /sbin/nologin

示例:以指定的格式輸入"/etc/passwd"文件中的內容

[root@localhost ~]# awk -F: '{printf "%-15s~%10s     ~%15s\n",$1,$3,$4}' /etc/passwd | head -3
root           ~         0     ~              0
bin            ~         1     ~              1
daemon         ~         2     ~              2

輸出重定向:

print items > output-file
print items >> output-file
print items | command

特殊文件描述符:

/dev/stdin:標準輸入
/dev/sdtout: 標準輸出
/dev/stderr: 錯誤輸出
/dev/fd/N: 某特定文件描述符,如/dev/stdin就相當於/dev/fd/0;

示例:下面兩種用法效果是一樣的:

[root@localhost ~]# awk -F: '{printf "%-15s %i\n",$1,$3,$NF > "/root/printf" }' /etc/passwd
[root@localhost ~]# awk -F: '{printf "%-15s %i\n",$1,$3,$NF}' /etc/passwd > /tmp/printf


六、AWK的操作符

1、算術操作符

操作符     描述
-x        負值
+x        轉換爲數值
x^y       次方
x**y      次方
x*y       乘法
x/y       除法
x+y       加法
x-y       減法
x%y       取餘

2、字符串操作符

只有一個,而且不用寫出來,用於實現字符串連接

示例:

[root@localhost ~]# awk '{print $1 $2}' printf | head -2
root0
bin1
[root@localhost ~]# awk '{print $1$2}' printf | head -2
root0
bin1

3、賦值操作符

操作符     描述
=         賦值操作符
+=        賦值加操作符
-=        賦值減操作符
*=        賦值乘操作符
/=        賦值除操作符
%=        賦值求餘操作符
^=        賦值求冪操作符
**=       賦值求冪操作符
注:如果某模式爲=號,此時使用/=/可能會有語法錯誤,應以/[=]/替代

4、布爾值

awk中,任何非0值或非空字符串都爲真,反之就爲假

5、比較操作符

操作符     描述
>         大於
<         小於
>=        大於等於
<=        小於等於
==        等於
!=        不等於
~         匹配
!~        匹配取反

6、表達式之間的邏輯關係符

&& : 邏輯與
|| : 邏輯或

示例:打印"/etc/passwd"文件中用戶uid小於100且以"r"開頭的行的用戶名及UID

[root@localhost ~]# awk -F: '$3<=100 && $1 $3 ~ /^r/{printf "%-5s %5s\n",$1,$3}' /etc/passwd
root      0

示例:打印"/etc/passwd"文件中用戶UID大於等於500或以"m"開頭的行

[root@localhost ~]# awk -F: '$3>=500 || $1 $3 ~ /^m/{printf "%-5s %5s\n",$1,$3}' /etc/passwd
mail      8
mockbuild   500
mysql   498

7、條件表達式

selector?if-true-exp:if-false-exp
if selector; then
  if-true-exp
else
  if-false-exp
fi

示例:在變量值中查找判斷

[root@localhost ~]# awk 'BEGIN{Name="welcome to china";print index(Name,"china")?"OK":"NO";}'
OK

8、函數調用

function_name (para1,para2)

上面所介紹的內容將在下面做示例介紹


七、AWK的模式

模式可以是表達式與正則表達式,還支持取反與模糊匹配等,如下:

常見的模式類型
Regexp: 正則表達式,格式爲/regular expression/
expresssion: 表達式,其值非0或爲非空字符時滿足條件,如:$1 ~ /foo/ 或 $1 == "allen",用運算符~(匹配)和!~(不匹配)
Ranges: 指定的匹配範圍,格式爲pat1,pat2
BEGIN/END:特殊模式,僅在awk命令執行前運行一次或結束前運行一次
Empty(空模式):匹配任意輸入行


   模式可以是以下任意一個:

/正則表達式/:使用通配符的擴展集。

關係表達式:可以用下面運算符表中的關係運算符進行操作,可以是字符串或數字的比較,如$2>%1選擇第二個字段比第一個字段長的行。

模式匹配表達式:

模式,模式:指定一個行的範圍。該語法不能包括BEGIN和END模式。

BEGIN:讓用戶指定在第一條輸入記錄被處理之前所發生的動作,通常可在這裏設置全局變量。

END:讓用戶在最後一條輸入記錄被讀取之後發生的動作。

   操作:

   操作由一個或多個命令、函數、表達式組成,之間由換行符或分號分隔,並位於花括號內,主要有四個部分:變量或數組賦值、輸出命令、內置函數、控制流命令

示例:打印"/etc/passwd"文件中以"bash"結尾和非"bash"結尾的行

[root@localhost ~]# awk -F: '/bash$/{print $1,$3,$NF}' /etc/passwd
root 0 /bin/bash
mockbuild 500 /bin/bash
mysql 498 /bin/bash
[root@localhost ~]# awk -F: '!/bash$/{print $1,$3,$NF}' /etc/passwd | head -3
bin 1 /sbin/nologin
daemon 2 /sbin/nologin
adm 3 /sbin/nologin

示例:打印UID包含1的用戶名及UID等於1的用戶名及UID

[root@localhost ~]# awk -F: '$3~1{print $1,$3}' /etc/passwd | head -3
bin 1
uucp 10
operator 11
[root@localhost ~]# awk -F: '$3==1{print $1,$3}' /etc/passwd | head -3
bin 1

其實除了用到過的表達式外還有ranges、BEGIN等,還可以使用以範圍匹配等

示例:打印"/etc/passwd"文件中以bin開頭到adm開頭中所有用戶的用戶名及UID與Shell

[root@localhost ~]# awk -F: '/^bin/,/^adm/{print $1,$3,$NF}' /etc/passwd
bin 1 /sbin/nologin
daemon 2 /sbin/nologin
adm 3 /sbin/nologin

示例:打印輸入結果添加註釋與結束符

[root@localhost ~]# awk -F: 'BEGIN{print "UserName:UID"}{print $1,"\t",$3}END{print "===END==="}' /etc/passwd
UserName:UID
root     0
bin      1
... ... ...
mysql    498
===END===

示例:打印用戶名以d開頭的,並顯示其UID,要有註釋信息

[root@localhost ~]# awk -F: 'BEGIN{print "UserName:UID"}$1 ~ /^d/{printf "%-10s%s\n",$1,$3}END{print "===END==="}' /etc/passwd
UserName:UID
daemon    2
dbus      81
===END===

示例:統計以shell爲"bash"的總用戶數

[root@localhost ~]# awk -F: 'BEGIN{sum=0}$NF ~ /bash$/{count++}END{print "SHELL is Bash:",count}' /etc/passwd
SHELL is Bash: 3

示例:使用BEGIN可以直接顯示字符也可以爲字段指定分隔符

[root@localhost ~]# awk 'BEGIN{print "All""en"}'
Allen
[root@localhost ~]# awk -v OFS=- 'BEGIN{print "All","en"}'
All-en
[root@localhost ~]# awk 'BEGIN{FS=":"}{print $1,$3}' /etc/passwd | head -2
root 0
bin 1

示例:打印系統用戶和普通用戶並輸出

[root@localhost ~]# awk -F: '{if ($3<500)print $1,"System User";else print $1 "Common User"}' /etc/passwd
root System User
... ... ...
mockbuild Common User
mysql System User
[root@localhost ~]# awk -F: '{if ($3==0)print $1,"Admin User";else if($3>0 && $3<500){print $1 " System User"}}' /etc/passwd
root Admin User
bin System User
daemon System User
... ... ...

示例:打印每行的奇數與偶數字段

[root@localhost ~]# awk -F: '{i=1;while(i<=NF){print $i;i+=2}}' /etc/passwd #奇
[root@localhost ~]# awk -F: '{i=2;while(i<=NF){print $i;i+=2}}' /etc/passwd #偶
[root@localhost ~]# awk -F: '{for(i=1;i<NF;i+=2)print $i}' /etc/passwd #奇
[root@localhost ~]# awk -F: '{for(i=2;i<NF;i+=2)print $i}' /etc/passwd #偶

示例:顯示ID爲奇數的用戶

awk -F: '{if($3%2==0) next;print $1,$3}' /etc/passwd

數組:

示例:數組引用,下標可以是字符串但需要雙引號,數字不需要

[root@localhost ~]# awk 'BEGIN{A[1]="hello";B[2]="world";print A[1],B[2]}'
[root@localhost ~]# awk 'BEGIN{A["a"]="hello";B["b"]="world";print A["a"],B["b"]}'

示例:被/^tcp/模式匹配到的行,數組S[$NF]就加1,NF爲當前匹配到的行的最後一個字段,此處用其值做爲數組S的元素索引

[root@localhost ~]# netstat -ant | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

示例:統計每個shell用戶的數量

awk -F: '$NF!~/^$/{BASH[$NF]++}END{for(A in BASH){printf "%15s:%i\n",A,BASH[A]}}' /etc/passwd

示例:計算100以內的加法

awk 'BEGIN{res=0;i=0;do{res+=i;i++;}while(i<=100)print res;}'
awk 'BEGIN{while(i<=100){res+=i;i++;}print res;}'

示例:統計日誌IP的訪問次數,最多的前6個

awk '{IP[$1]++}END{for(A in IP)print IP[A],A}' access_log|sort -rn|head -6

AWK內置函數:

split(string, array [, fieldsep [, seps ] ])

功能:將string表示的字符串以fieldsep爲分隔符進行分隔,並將分隔後的結果保存至array爲名的數組中;數組下標爲從1開始的序列;

netstat -ant | awk '/:80\>/{split($5,clients,":");IP[clients[1]]++}END{for(i in IP){print IP[i],i}}' | sort -rn | head -50

length([string])

功能:返回string字符串中字符的個數;

substr(string, start [, length])

功能:取string字符串中的子串,從start開始,取length個;start從1開始計數;

system(command)

功能:執行系統command並將結果返回至awk命令

systime()

功能:取系統當前時間

tolower(s)

功能:將s中的所有字母轉爲小寫

toupper(s)

功能:將s中的所有字母轉爲大寫


示例:讀取100以內隨機數

awk 'BEGIN{srand();fr=int(100*rand());print fr;}'

示例:正則表達式,match函數的使用

[root@localhost ~]# awk 'BEGIN{res="welcome to china!";print match(res,/[0-9]+/)?"ok":"no";}'
no
[root@localhost ~]# awk 'BEGIN{res="welcome to 123 china!";print match(res,/[0-9]+/)?"ok":"no";}'
ok

示例:截取字符串,從第12個字符向後截取5個字符長度

[root@localhost ~]# awk 'BEGIN{info="welcome to china!";print substr(info,12,5);}'
china

示例:顯示/etc/passwd中的前三個字段

[root@localhost ~]# awk -F: '{for(i=1;i<=3;i++) print $i}' /etc/passwd
[root@localhost ~]# awk -F: '{for(i=1;i<=NF;i++) { if (length($i)>=4) {print $i}}}' /etc/passwd

示例:調用外部命令執行,顯示磁盤使用信息

[root@localhost ~]# awk 'BEGIN{disk=system("df -h");print disk;}'
文件系統          容量  已用  可用 已用%% 掛載點
/dev/mapper/VolGroup-lv_root
                       50G   12G   36G  24% /
tmpfs                 194M     0  194M   0% /dev/shm
/dev/sda1             485M   45M  416M  10% /boot
/dev/mapper/VolGroup-lv_home
                       67G  180M   63G   1% /home
0

示例:時間函數strftime、systime的使用

[root@localhost ~]#  awk 'BEGIN{Tim=mktime("2013 09 04 13 06 26");print strftime("%c",Tim);}'
2013年09月04日 星期三 13時06分26秒
[root@localhost ~]# date
2013年 09月 04日 星期三 14:09:35 CST
[root@localhost ~]# awk 'BEGIN{Time=mktime("2013 09 04 13 06 26");Time1=systime();print Time1-Time;}'
3791

到此,以上內容爲簡寫,如果都能理解並靈活使用那就最好了,這也是最基本的使用,如果想熟練使用並不是一天兩天能搞定的,一起學習吧!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章