awk是Linux中的一個命令,用來做文本處理與分析,功能簡單強悍,同時它也是一門編程語言。
awk處理文本文件時,以行為單位,可以高效的對日志文件進行處理。
awk的man文檔簡介摘要:
NAME
gawk - pattern scanning and processing language //awk其實是gawk,文本匹配查詢和
處理語言,
SYNOPSIS
gawk [ POSIX or GNU style options ] -f program-file [ -- ] file ...
gawk [ POSIX or GNU style options ] [ -- ] program-text file ...
pgawk [ POSIX or GNU style options ] -f program-file [ -- ] file ...
pgawk [ POSIX or GNU style options ] [ -- ] program-text file ...
Gawk is the GNU Project?. implementation of the AWK programming language. It
conforms to the definition of the lan-
guage in the POSIX 1003.1 Standard. This version in turn is based on the
description in The AWK Programming Lan-
guage, by Aho, Kernighan, and Weinberger, with the additional features
found in the System V Release 4 version of
UNIX awk. Gawk also provides more recent Bell Laboratories awk extensions,
and a number of GNU-specific extensions.
【釋】第一段英文大致說了,Gawk是GNU(通用許可開源)項目,源於unix awk,並支持更多的貝爾實驗
室awk的擴展。看起來Linux中的awk(gawk)是很牛叉的。廢話不多說,馬上就來認真學習一下。
命令常見使用方式如下
方式一:awk [options] 'script' [var=value] file(s)
方式二:awk [options] -f scriptfile [var=value] file(s)
【解釋】 其中 options是命令選項。方式一:'script'指awk的處理程序,執行程序。var=value是
變量,file(s) 表示被處理的文本文件,可以一次性處理多個文本文件。方式二中-f scriptfile
表示將處理程序放到一個文本中,當作腳本文件。
指定分隔符,awk中的默認分隔符是空格 ” “,默認以空格符分隔行文本。
選項在man文檔中的摘要
-F fs
--field-separator fs //指定分隔符
Use fs for the input field separator (the value of the FS prede-
fined variable).
eg
[案例1] //默認字段分隔符為" "
# echo "hello hh"| awk '{print $1;print $2}'
hello
hh
【解釋】 案例中,並沒有出現-F選項,此時默認分隔符為" " 將hello hh分隔為兩段,再分別打印
字段一:“hello”,再打印字段二:“hh”。$1和$2是位置變量,分別表示第一個字段和第二個字段。
[案例2]
# echo "hello:hh"| awk -F : '{print $1;print $2}'
hello
hh
【解釋】通過-F指定分隔符為":" 分隔 hello:hh ,然後同 [案例1]
eg
[案例1]
# cat pass.log //要處理的目標文件 pass.log 筆者截取了一段/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
# awk -F : '{print ;}' pass.log
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
【解釋】 "print;" 默認也是打印整行,同$0
[案例2]
# awk -F : '{print $0;}' pass.log
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
【解釋】 "print $0;" 打印整行
eg
# awk -F : '{print $1 " " $NF;}' pass.log
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
【解釋】打印被 ":" 分隔後第一個位置上的字符串,然後再打印最後一個位置,中間輸入空格方便查看
將腳本寫到文本文件中去,通過選項-f將awk命令的處理程序指定到某個腳本文件中,當處理程序復雜的時候通常采用這種方式。
man文檔摘要
-f program-file
--file program-file //--file 和-f效果相同
Read the AWK program source from the file program-file, instead
of from the first command line argument. Multiple -f (or
--file) options may be used.
eg
[案例一]
# vi sc.awk //編輯腳本
{print $1 " " $NF} //將執行文件寫入 sc.awk中
//vi編輯器,保存sc.awk文檔退出。
# awk -F : -f sc.awk pass.log //pass.log和前文中pass.log相同。
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
【解釋】 將前文中案例 【#awk -F : '{print $1 " " $NF;}' pass.log】 中的
'{print $1 " " $NF;}'寫到了 sc.awk中,此時,處理程序不用再用放在單引號''中了。
【注意】如果對vi不熟悉,可以用echo "{print $1 " " $NF}" >sc.awk 來新建程序文件
awk處理程序由 BEGIN,主體,END 構成
BEGIN語句塊 BEGIN{}
eg
# vi sc.awk //編輯腳本
# awk program's Begin block //【注意】這句話是腳本裡的注釋,不是命令
BEGIN{print "here is the awk begin!!"} //BEGIN語句快
{print $1 " " $NF}
# awk -F : -f sc.awk pass.log
here is the awk begin!! //BEGIN語句塊 的輸出效果
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
【解釋】 上面案例可以看出,BEGIN語句塊中的代碼會在程序最前面執行。且只執行一次。
主體部分,主題部分用 {} 表示,寫在BEGIN之後,END之前。前文中使用的’{print}’都是程序主體。
eg
# vi sc.awk //編輯腳本
# awk program's main block //【注意】這句話是腳本裡的注釋,不是命令
BEGIN{print "here is the awk begin!!"}
{
print "this is main"
print $1
}
# awk -F : -f sc.awk pass.log
here is the awk begin!!
this is main // 執行 主體部分中的 print "this is main"
root // 執行 主體中的print $1
this is main
bin
this is main
daemon
【解釋】 主體中的代碼,沒處理一行就會被執行一次。
END語句塊
和BEGIN語句塊相似 END{} 命令最後會被執行,也僅被執行一次
eg
# vi sc.awk //編輯腳本
# awk program's end block //【注意】這句話是腳本裡的注釋,不是命令
BEGIN{print "here is the awk begin!!"}
{
print $1
}
END{
print "ok,awk operate to the end" //end模塊打印語句
}
# awk -F : -f sc.awk pass.log
here is the awk begin!!
root
bin
daemon
ok,awk operate to the end //end模塊的執行效果
在awk的處理程序中可以申明變量,和使用變量。
申明方式 varName=varValue
eg | 通過變量,計算文件的行數
# vi sc.awk //編輯腳本
# awk program's var block //【注意】這句話是腳本裡的注釋,不是命令
BEGIN{
nu=0; //申明一個變量表示行號
}
{
nu ++; //每處理一行數據,行號自動增加1
print nu " " $0 //輸出行號和 第一個字段
}
END {
print "totall lines is " nu; //最後打印總共的行數,即最後一行行號
}
# awk -F : -f sc.awk pass.log
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
totall lines is 3
【解釋】 nu=0為變量申明,使用的時候,直接使用nu即可。不用像bash裡用$打頭
我把它叫做命令變量,是因為變量是從命令中傳入的,而並非awk命令中自定義的。
從命令外部引入變量,可以通過命令選項 [var=value] 把shell中的變量引入到awk處理程序中
方式一: awk [options] -v varname=varvalue 'script' file(s)
方式二: awk [options] 'script' varname=varvalue file(s)
【解釋】方式一是通過-v選項傳入,方式二是 man手冊裡的選項[--]
eg | 方式一
# awk -F : -v aa="haima" '{print aa " " $1}' pass.log
haima root
haima bin
haima daemon
【解釋】 {print aa " " $1} 打印了命令中傳入的變量aa,aa的值是haima。然後再打印 $1
eg | 方式二
# awk -F : '{print aa " " $1}' aa="haima" pass.log
haima root
haima bin
haima daemon
內置變量是awk程序中系統自帶的變量,分別都有各自的意義
FS 字段分割符,在程序中【FS=":";】相當於 命令中使用 【-F :】
IGNORECASE 是否忽略大小寫,IGNORECASE=true則匹配時不區分大小寫
NF 表示字段數,在執行過程中對應於當前的字段數
NR 表示記錄數,在執行過程中對應於當前的行號
OFS 輸出字段分隔符(默認值是一個空格)
ORS 輸出行記錄分隔符(默認值是一個換行符)
IGNORECASE在筆者系統上沒測試通過 centos6.5
eg | FS
# vi sc.awk
# awk program's FS //【注意】這句話是腳本裡的注釋,不是命令
BEGIN{
FS=":" //用HAIMA 去分割,
}
{
print $1
}
# awk -f sc.awk pass2.log
root
bin
daemon
【解釋】 FS=":" 需要在BEGIN中指定,效果與令行中使用 -F :相同
eg | NF NR
# vi sc.awk
# awk program's NF NR //【注意】這句話是腳本裡的注釋,不是命令
{
print NR " totall fileds" NF // 打印行號,打印切割後的段
}
END{
print "line numbers:" NR //END中能打印到行數
}
# awk -F: -f sc.awk pass.log
1 totall files:7
2 totall files:7
3 totall files:7
line numbers:3
【注意】 在END中不能打印到字段數NF
eg|OFS ORS
# awk -F : 'BEGIN{OFS="|";ORS=" LinEnd\n"}{print $1, $2;}' pass.log
root|x LinEnd
bin|x LinEnd
daemon|x LinEnd
【解釋】 在BEGIN模塊中申明了OFS="|",輸出字段分隔符是“|” ,而 ORS=" LinEnd\n" 申明了輸出
行分隔符為"linEnd\n" 其中"\n"是 換行符,如果沒有“\n” ,將直接輸出一行
【注意】 “{print $1, $2;}” 時,必須用 "," 連接。如果 {print $1 $2} 字段分隔符將失效。因
為空格在awk程序中可以連接兩個字符串。
通過前面的案例,已經大概了解awk的功能。下面再總結一下awk的工作流程。
awk 'BEGIN{ commands } pattern{ commands } END{ commands }'
第一步:執行BEGIN{ commands }語句塊中的語句;
第二步:從文件或標准輸入(stdin)讀取一行,然後執行pattern{ commands }語句塊,它逐行掃描
文件,從第一行到最後一行重復這個過程,直到文件全部被讀取完畢。
第三步:當讀至輸入流末尾時,執行END{ commands }語句塊。
BEGIN語句塊在awk開始從輸入流中讀取行之前被執行,這是一個可選的語句塊,比如變量初始化、打
印輸出表格的表頭等語句通常可以寫在BEGIN語句塊中。
END語句塊在awk從輸入流中讀取完所有的行之後即被執行,比如打印所有行的分析結果這類信息匯總
都是在END語句塊中完成,它也是一個可選語句塊。
pattern語句塊中的通用命令是最重要的部分,它也是可選的。如果沒有提供pattern語句塊,則默認
執行{ print },即打印每一個讀取到的行,awk讀取的每一行都會執行該語句塊。
【注意】awk中的內置變量。不同的內置變量的作用域有所不同。使用的時候,需要注意在awk執行的某
些階段,該內置變量是否可用。
if … else if… else
awk可以用類似java中的if語句來做邏輯控制
語法結構如下:
if(表達式)
{語句1}
else if(表達式)
{語句2}
else
{語句3}
eg
# vi nsc.awk //新建一個awk程序
# test for "if.. else if.. else " //程序注釋,非指令
{
if(NR%2==0)
{ print $2 " evenline"} //偶數行就 輸出第二個字段和 evenline
else if (NR%3==0)
{print $3 " thrirdline"} //被3整除的行,就輸出第三個字段 和 thrirdline
else
{print $NF " othersLine"} //其他就輸出 最後一個字段 和 othersLine
# awk -F : -f nsc.awk /etc/passwd
/bin/bash othersLine
x evenline
2 thrirdline
x evenline
/sbin/nologin othersLine
awk也支持for,while,do..while三種循環語句,語法與java中基本相同
格式: for(變量;條件;表達式) {語句}
# awk 'BEGIN{ total=0; i=0; do {total+=i;i++;} while(i<=100) print total; }'
5050
格式:while(表達式) {語句}
# awk 'BEGIN{ test=100; total=0; while(i<=test){ total+=i; i++; } print total; }'
5050
格式:do {語句} while(條件)
# awk 'BEGIN{ total=0; i=0; do {total+=i;i++;} while(i<=100) print total; }'
5050
# awk 'BEGIN{ total=0; i=1000; do {total+=i;i++;} while(i<=100) print total; }'
1000 //雖然i一開始就滿足i<=100 但do中還是執行了一次,所以打印total為 1000
【注意】do.. while 循環語句至少一次循環,先執行do裡面的內容,再判斷,所以這樣。
awk同樣也支持break和continue控制。break和continue必須在循環體中使用。
break是直接跳出循環,不再執行循環
continue是跳到下次循環
next 從單詞的意思看起來和我continue相似。確實如此,next具有continue很相似的功能,只是next是跳出本行數據處理,處理下一行數據,而continue是跳出本次循環,執行下次循環。
eg | 跳過 行號為6-9行的數據
# awk -F : '{if(NR>5&&NR<10)next; print NR, $1}' /etc/passwd
1 root
2 bin
3 daemon
4 adm
5 lp
10 uucp
11 operator
12 games
13 gopher
14 ftp
15 nobody
【解釋】 if(NR>5&&NR<10)next; 當行號在 6-10,直接跳過本行數據的處理。
【注意】 因為next是跳過某個記錄,或者說某行數據的處理,所以next只能出現在awk的主體中,不能
出現在BEGIN 塊,也不能出現在 END模塊中,會報錯。
退出當前還沒有執行的主體內容,如果END塊中有內容,則執行END塊中的內容。此時就會出現三種情況。
情況一:exit在BEGIN塊中使用,從exit當前位置退出,直接執行END塊
情況二:exit在主體中使用,從exit當前位置退出,直接執行END塊
情況二:exit在END塊中使用,從exit當前位置退出,已經退出END快,所以不再執行了。
eg | exit在主體中使用。
# awk -F : '{if(NR>5){exit};print NR, $1}END{print "end"}' /etc/passwd
1 root
2 bin
3 daemon
4 adm
5 lp
end
【解釋】當行號NR>5時,退出主體,再執行print "end"
eg | exit在BEGIN或END中使用
# awk -F : '{print"begin";exit;print"hh"}{print NR, $1}END{print "end"}' /etc/passwd
begin
end
【解釋】print "begin" 執行完後,直接跳到了END模塊執行 print "end"
awk可以利用正則表達式來篩選數據,以達到更強大的處理能力
記錄篩選,以一條記錄作為篩選的單位
awk [options] ‘script’ [var=value] file(s) 在此主體格式不變的情況下,在script前面加入篩
選,此時,’script’ 程序形式變成:’(/express/){awk處理程序}’,其中/express/是正則表達式
手動創建一個文件target.log 並寫好內容如下
# cat target.log //查看target.log
27.19.74.143 - - GET /static/image/common/faq.gif HTTP/1.1" 200 1127
110.52.250.126 - GET /data/cache/style_1_widthauto.css?y7a HTTP/1.1" 200 1292
27.19.74.143 - - GET /static/image/common/hot_1.gif HTTP/1.1" 200 680
27.19.74.143 - - GET /static/image/common/hot_2.gif HTTP/1.1" 200 682
27.19.74.143 - - GET /static/image/filetype/common.gif HTTP/1.1" 200 90
110.52.250.126 - GET /source/plugin/wsh_wx/img/wsh_zk.css HTTP/1.1" 200 1482
110.52.250.126 - GET /data/cache/style_1_forum_index.css?y7a HTTP/1.1" 200 2331
110.52.250.126 - GET /source/plugin/wsh_wx/img/wx_jqr.gif HTTP/1.1" 200 1770
27.19.74.143 - - GET /static/image/common/recommend_1.gif HTTP/1.1" 200 1030
110.52.250.126 - GET /static/image/common/logo.png HTTP/1.1" 200 4542
110.44 - GET /static/image/common/logo.png HTTP/1.1" 200 4542
【注意】 最後一行數據是 不正常的數據。ip 的格式不對
eg | 篩選出包含.gif的行
# awk '(/gif/){print $0}' target.log
27.19.74.143 - - GET /static/image/common/faq.gif HTTP/1.1" 200 1127
27.19.74.143 - - GET /static/image/common/hot_1.gif HTTP/1.1" 200 680
27.19.74.143 - - GET /static/image/common/hot_2.gif HTTP/1.1" 200 682
27.19.74.143 - - GET /static/image/filetype/common.gif HTTP/1.1" 200 90
110.52.250.126 - GET /source/plugin/wsh_wx/img/wx_jqr.gif HTTP/1.1" 200 1770
27.19.74.143 - - GET /static/image/common/recommend_1.gif HTTP/1.1" 200 1030
eg | 篩選出數據不正常的行(target.log中最後一行數據為錯誤數據)
#awk --posix '!/([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}/{print $0}' target.log
110.44 - GET /static/image/common/logo.png HTTP/1.1" 200 4542
【解釋】 篩選行,首先選出行中含有IP格式,ip格式用/([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}/
匹配,前面加 ! 表示取非,也就是不含ip格式的行,被打印出來
【注意】筆者查詢了一下, 在gawk 4.0以下版本中使用{n,m}這樣的正則,需要在在前面加上
--re-interval或--posix選項。
awk也可通過篩選某個字段,來篩選某一行
在正則表達式前面
使用
eg| 用 “:” 切割/etc/passwd,篩選出用戶名長度為4的用戶
# awk --posix -F : '$1~/^.{4}$/{print $1}' /etc/passwd
root
sync
halt
mail
uucp
dbus
vcsa
abrt
sshd
【注意】筆者查詢了一下, 在gawk 4.0以下版本中使用^$這樣的正則,需要在在前面加上
--re-interval或--posix選項。
eg| 用 “:” 切割/etc/passwd,篩選出用戶名長度不為4的用戶
# awk --posix -F : '$1!~/^.{4}$/{print $1}' /etc/passwd
bin
daemon
adm
lp
shutdown
operator
games
gopher
ftp
nobody
usbmuxd
rtkit
//此處省略部分結果行
【注意】筆者查詢了一下, 在gawk 4.0以下版本中使用^$這樣的正則,需要在在前面加上
--re-interval或--posix選項。
正則表達式用在返回值為true/false的表達式中,同樣用~,!~匹配,匹配成功則返回true
eg | 處理/etc/passwd 匹配行數為二十多的
# awk --posix -F : '{if(NR ~ /^[2][0-9]$/)print NR,$1}' /etc/passwd
20 avahi-autoipd
21 abrt
22 haldaemon
23 gdm
24 saslauth
25 postfix
26 ntp
27 apache
28 pulse
29 sshd
【解釋】 NR為內置變量行號,行號匹配正則表達式為/^[2][0-9]$/,則打印行號NR,第一個字段