awkで色々な処理を行う。

UNIX/Linuxでシェルスクリプトじゃ出来ないけども、ちょっと複雑なことをしたい。

でも、Javaとかで作りこむほどではない。

そんなときにawkを使うと便利だ。

 

例えばこんな感じのアクセスログがあって、平均の応答時間を求めたい場合時など。

# 末尾の数字を応答時間(秒)とする。

111.22.33.44 - - [11/Jan/2017:09:00:00 +0900] "GET /index.html" 200 1472 10
111.22.33.44 - - [11/Jan/2017:09:01:00 +0900] "GET /index.html" 200 1472 5
111.22.33.45 - - [11/Jan/2017:09:02:00 +0900] "GET /index.htm" 404 1472 0
111.22.33.45 - - [11/Jan/2017:09:02:00 +0900] "GET /index.html" 200 1472 3

awkを使えば一行で処理ができる。

 

awk 'BEGIN { i=0 } { i=i+$NF } END { print "avg " i/NR " sec"}' <入力ログファイル名>

 

簡単に解説すると、以下のようなことをしている。

BEGIN { i=0 } : 処理を開始する前の初期設定

{ i=i+$NF } : awk は入力ファイルを1行ずつ読み込んで処理をする。

       読み込んだパラメータは、区切り文字(デフォルトでは半角スペース)に従い自動的に分割し、変数に格納される。

       $0 は行全体、$1以降はそれぞれ数字の分の要素になる。

       NF は行全体の要素数が自動的に入る。したがって、$NFは自動的に末尾の数字になる。

       ここでは、1行ごとに読み込んで変数「i」に加算している。

END { print "avg " i/NR " sec" } :

       END は、全ての行を読み込んだ後に処理される。

       NR は、処理を行った行数が自動的に入る。ENDまで来ると、自動的にファイルの行数になる。

       なので、これで応答時間の合計を除算すると平均の時間になる。

 

これを実行すると、こんな感じの結果になる。

 

[root@localhost ~]# cat test.log
111.22.33.44 - - [11/Jan/2017:09:00:00 +0900] "GET /index.html" 200 1472 10
111.22.33.44 - - [11/Jan/2017:09:01:00 +0900] "GET /index.html" 200 1472 5
111.22.33.45 - - [11/Jan/2017:09:02:00 +0900] "GET /index.htm" 404 1472 0
111.22.33.45 - - [11/Jan/2017:09:02:00 +0900] "GET /index.html" 200 1472 3
[root@localhost ~]#
[root@localhost ~]# awk 'BEGIN { i=0 } { i=i+$NF } END { print "avg " i/NR " sec"}' test.log
avg 4.5 sec
[root@localhost ~]#

 

4行程度ではわざわざこんなことする方が面倒なのだけども、実際のアクセスログだと何万行を軽く超えるような環境もあるので、

そんな場合ではいちいちローカルPCにダウンロードしてきてExcelとか使って解析するよりも、サーバー上でパパッと処理してしまった方が早い。サーバーリソースには要注意だけども……

 

また、awkは正規表現を使って特定行のみを処理するということも出来るので、特定IPからの平均応答時間のみを集計することなども可能。

 

awk 'BEGIN { i=0;j=0 } $1 == "111.22.33.45" { i=i+$NF;j=j+1 } END { print "avg " i/j " sec"}' <ログファイル名>

 

先ほどのawk文と比べると、変数が一個増えて、BIGIN{}の後に正規表現が追加されている。

こうすると、マッチングする行のみを対象として処理を行う用になる。

では、実行してみよう。

 

[root@localhost ~]# awk 'BEGIN { i=0;j=0 } $1 == "111.22.33.45" { i=i+$NF;j=j+1 } END { print "avg " i/j " sec"}' test.log
avg 1.5 sec
[root@localhost ~]#

先ほどと同じログに対して実行しても、違う結果が返ってきた。

このように、特定の条件にマッチングするリクエストのみに対して処理が出来たりもする。

IP以外にもHTTPレスポンスコードから抽出したり、一定以上の応答時間のアクセスを抽出したりもできる。

 

#404以外のアクセスを抽出

[root@localhost ~]# awk '$8 != "404" { print $0 }' test.log
111.22.33.44 - - [11/Jan/2017:09:00:00 +0900] "GET /index.html" 200 1472 10
111.22.33.44 - - [11/Jan/2017:09:01:00 +0900] "GET /index.html" 200 1472 5
111.22.33.45 - - [11/Jan/2017:09:02:00 +0900] "GET /index.html" 200 1472 3

[root@localhost ~]#

 

#5秒以上の応答時間のアクセスを抽出

[root@localhost ~]# awk '$NF >= 5 { print $0 }' test.log
111.22.33.44 - - [11/Jan/2017:09:00:00 +0900] "GET /index.html" 200 1472 10
111.22.33.44 - - [11/Jan/2017:09:01:00 +0900] "GET /index.html" 200 1472 5

[root@localhost ~]#

 

余分なデータを除外したり、特定のデータを抽出することを繰り返して解析の精度を上げたりしていける。

awk はコマンドラインから実行できるので、シェルに組み込むことも簡単だ。

一度実行した処理をまとめてシェルにしておけば、次回以降同じ作業を簡単に実施できる。

定例的な処理とか、何か問題が起きた時に使うモノを纏めておくと便利でかっこいいんじゃないかな。