AWK高階應用

mgoann發表於2020-04-06

AWK高階應用

 

在進行資料割接時,需要對其匯出指令碼的日誌檔案進行統計分析以便核對是否有資料沒有匯出的情況!該日誌檔案的格式都是固定的,可以使用指令碼來完成統計分析,而且指令碼很靈活小巧。

 

         因為其複雜的語法和不明確的錯誤提示,造成awk的使用者進而遠之,awk整體上比較難掌握。Awk是一種自解釋的程式語言。而awk強大的文字處理功能正好能夠勝任該工作。如果要格式化報文或從一個大的文字檔案中抽取資料包,那麼awk可以完成這些任務。它在文字瀏覽和資料的熟練使用上效能優異。

 

 

Awk的特性網上有很多資料,大家可以參考一下。本次所運用的awk的特性有:

Awk陣列、printf修飾符、內建字串函式、awk操作符、條件操作符。

 

         要想使用好awk,必須對awk的語法有一定的認識,否則你會被一大堆莫名其妙的錯誤所包圍。

 

Awk語句都是由模式和動作組成。模式的包括倆個關鍵字,BEGIN和END。BEGIN模式在awk遍歷文字前呼叫,用來列印一些頭資訊或是宣告一些全域性變數。例如:

 

Log.dat記錄著日誌資訊,日誌為日期-ip-手機號碼。

linux:/home/dss/dss/logs/msgs> cat log.dat

20091111-172230-665-10.168.38.63-15035198115

20091111-172230-738-10.168.38.65-13840784654

20091111-172238-571-10.168.38.63-15929933330

20091111-172238-668-10.168.38.63-13666463997

20091111-172240-262-10.168.38.68-13591931301

20091111-172242-24-10.168.38.63-15041615076

20091111-172248-427-10.168.38.63-13409199466

 

 

如果我想統計來自ip10.168.38.63的日誌有幾個,命令如下:

linux:/home/dss/dss/logs/msgs> awk -F'-' 'BEGIN {printf "%-15s %s\n","IP","LOG"printf “===============================================”}{if($4~/10.168.38.63/)printf "%-15s %s\n", $4,$0}END{}' log.dat

IP              LOG

10.168.38.63    20091111-172230-665-10.168.38.63-15035198115

10.168.38.63    20091111-172238-571-10.168.38.63-15929933330

10.168.38.63    20091111-172238-668-10.168.38.63-13666463997

10.168.38.63    20091111-172242-24-10.168.38.63-15041615076

10.168.38.63    20091111-172248-427-10.168.38.63-13409199466

 

其中頭資訊IP和LOG在遍歷log.dat前列印!

而END模式在遍歷完檔案最後執行,可以用於資訊彙總,或列印結束日誌。

 

實際動作需要包含在{}之間,如果不指明,預設動作是列印所有行。

 

Awk的域和記錄,awk執行時,其瀏覽域標記為$1,$2… $n。這種方法稱為域標識。使用這些域標識將更容易對域進行進一步處理。$0為匹配整行,看個例子:

 

linux:/home/dss/dss/logs/msgs> awk -F'-' '{print $4, $5}' log.dat

10.168.38.63 15035198115

10.168.38.65 13840784654

10.168.38.63 15929933330

10.168.38.63 13666463997

10.168.38.68 13591931301

10.168.38.63 15041615076

10.168.38.63 13409199466

 

以上命令只列印第4列和第5列。並且域分隔符為-,預設的域分隔符為空格,使用-F引數指定域分隔符。

awk有許多內建變數用來設定環境資訊。這些變數可以被改變。下面列出一些最常使用的一些變數,並給出其基本含義。

ARGC 命令列引數個數

ARGV 命令列引數排列

ENVIRON 支援佇列中系統環境變數的使用

FILENAME awk瀏覽的檔名

FNR 瀏覽檔案的記錄數

FS 設定輸入域分隔符,等價於命令列- F選項

NF 瀏覽記錄的域個數

NR 已讀的記錄數

OFS 輸出域分隔符

ORS 輸出記錄分隔符

RS 控制記錄分隔符

 

awk有許多強大的字串函式,這些字串函式在處理文字方面起著很重要的作用。通過這些內建字串處理函式可以很隨意的定義一些自己想要的函式。下面為awk中內建字串處理函式。

gsub (r, s) 在整個$0中用s替代r

gsub (r, s, t) 在整個t中用s替代r

index (s , t) 返回s中字串t的第一位置

length ( s ) 返回s長度

match (s , r) 測試s是否包含匹配r的字串

split (s , a , fs) 在f s上將s分成序列a

sprint (fmt , exp) 返回經f m t格式化後的e x p

sub (r, s) 用$ 0中最左邊最長的子串代替s

substr (s , p) 返回字串s中從p開始的字尾部分

substr (s , p , n) 返回字串s中從p開始長度為n的字尾部分

 

如下我只要使用index和substr函式就可以組合出很強大的自定義函式:

function substrmid(src, begin, end) {

        startindex=index(src,begin) + length(begin)

        temp=substr(src,startindex)

        endindex=index(temp,end) - length(end)

        return substr(src,startindex,endindex)

    }

   

    function substrbefore(src, end) {

        endindex=index(src,end)-length(end)

        return substr(src,0,endindex)

    }

   

    function substrafter(src, begin) {

        startindex=index(src,begin) + length(begin)

        return substr(src,startindex)

}

 

自定義函式substrbefore(src, end)是擷取src中end之前的字串,substrmid(src, begin, end)是擷取src中介於begin和end之間的字串(不包含begin和end),substrend(src, end)是擷取src中end之後的字元換,例如:

Substrmid(“2009-11-01”, “-”,”-”)返回字串11,substrbefore(“2009-11-01”, “-”)返回2009,substrafter(“11-01”, “-”)返回01,可以根據自己的需要構造一些自定義函式。

 

最後簡單介紹一下awk中的陣列應用。Awk是一種類似自解釋語言,其中的資料應用可以很好的完成資料統計工作。Awk中陣列使用無需提前宣告。陣列使用前,不必定義,也不必指定陣列元素個數。經常使用迴圈來訪問陣列。下面是一種迴圈型別的基本結構

For (element in array ) print array[element]

Awk的特性介紹完了,現在開始實戰了!

 

這個小工具的要求是這樣的,在做資料割接時,將資料庫的資料按照路由規格利用資料庫提供的批量匯出工具把資料匯出成dat檔案,需要對資料庫的日誌進行統計分析,看看每個分庫每個分表匯出多少條記錄!

 

SQL3104N  The Export utility is beginning to export data to file

"./PISADB10/CONTACTINFO977_1_3717.dat".

中間有若干行無關緊要的內容

Number of rows exported: 24040

其中的./PISADB10/CONTACTINFO977_1_3717.dat和Number of rows exported: 24040為關鍵資訊。

PISADB10表示分庫10,CONTACTINFO977_1_3717.dat表示分表1。而Number of rows exported: 24040為匯出多少行。而日誌檔案裡有上萬行這樣的日誌,而且大概有100多個日誌,要求這個小功能能夠做到批量統計。

 

首先使用grep命令將相關重要資訊提取出來,以免其他資訊造成干擾。使用命令

Grep –E ‘^Number of rows exported|^".*(CONTACTINFO|DS_CLIENT_MAPPING|GROUPCONTACTMAP)\w*.dat’ db2export_1.sh.log

命令輸出格式為:

。。。

"./PISADB5/CONTACTINFO472_1_12012.dat".

Number of rows exported: 40

"./PISADB5/CONTACTINFO473_1_12033.dat".

Number of rows exported: 24040

"./PISADB5/CONTACTINFO474_1_12054.dat".

Number of rows exported: 40

"./PISADB5/CONTACTINFO475_1_12075.dat".

Number of rows exported: 40

"./PISADB5/CONTACTINFO476_1_12096.dat".

Number of rows exported: 40

 

以上資訊才是我想要的。將結果交給awk來處理完成,由於awk本身的機制,awk只能按行來處理檔案,一次只能處理一行,所有以上資訊處理還需要一些特殊的適配。Awk一次只讀一樣,如果該行不包含Number說明該行為分庫和分表資訊行,將其暫存起來,等到下一行Number行時,再對其進行處理。這裡可以使用awk中的條件操作符if語句來完成,{if($0!~/Number/) tmp=$0} {if($0~/Number/)print “This is a number line and it’s owner is ”tmp},其中if的條件判斷無需用()引起來,但是未避免語句錯誤最好將所有的條件判斷都用()擴起來。!~為不匹配~為匹配,//之間可以輸入正規表示式。

 

當行為number行時,需要將其中的關鍵數字擷取出來做彙總。

if($0!~/Number/){

    num=substrafter($0,” exported: ”)

}

這樣就將num擷取出來了,剩下的就是做彙總資訊了!完整的指令碼如下:

 

 grep -E '^Number of rows exported|^".*(CONTACTINFO|DS_CLIENT_MAPPING|GROUPCONTACTMAP)\w*.dat' db2export_1.sh.log | awk '

 

    function substrmid(src, begin, end) {

        startindex=index(src,begin) + length(begin)

        temp=substr(src,startindex)

        endindex=index(temp,end) - length(end)

        return substr(src,startindex,endindex)

    }

   

    function substrbefore(src, end) {

        endindex=index(src,end)-length(end)

        return substr(src,0,endindex)

    }

   

    function substrafter(src, begin) {

        startindex=index(src,begin) + length(begin)

        return substr(src,startindex)

 

BEGIN {

    flag=""

    total=0

    table=""

}

 

{

    if($0!~/Number/) {

        flag=$0

    }

}

 

{

    if($0~/Number/) {

    print $0

    num=substrafter($0,"exported: ")

    partdb=substrmid(flag, "/", "/")

    fromtable=substrmid(flag, "_", "_")

    P[partdb]=P[partdb]+num

    S[fromtable]=S[fromtable]+num

    total=total+num

    }

}

 

END {

    print "Total number of export: "total

    for(p in P){

        print p" number of export: "P[p]

    }

    for(s in S){

        print "parttable "s"  number of export:  "S[s]

    }

}

相關文章