こんにちは、河野です。
保守業務でログを分割して一部のフィールドを閲覧・集計することがありますが、そんなときはawkが活躍しますね。今回はawkの使い方を紹介します。
まずはawkの基本をおさらい
awkスクリプトの書式
awkは、何を実行するかawkスクリプトを記述する必要があります。
| 1 2 3 | 条件 {   実行内容 } | 
こういったブロックを複数記述することができ、入力されたテキスト行が条件に一致したときにブロックが実行されます。
良く見かけるのは、
| 1 | cat access.log | awk '{print $1}' | 
というものですが、これは条件を省略していることになります。入力されたテキストの全行に対して実行するということですね。
また特殊な条件として、BEGIN, ENDがあり、それぞれ最初と最後に1度だけ実行されます。実行前の初期設定を行ったり、最終的な集計結果を出力したりという使い方ができます。
実行形式ファイルにする
また、実行形式のファイルとして記述することもできます。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | $ vi sum.awk #!/bin/awk -f BEGIN {   print "Summary"   print "----------" } {   total += $2 } END {   print "Count:", NR   print "Total:", total } | 
実行は、
| 1 2 3 4 5 | $ cat log | ./sum.awk Summary ---------- Count: 3 Total: 6 | 
といった感じです。
区切り文字を変更する
区切り文字を変更するには -F オプションを使います。
例:CSVのアンケート結果から一部だけ出力
| 1 2 3 4 5 6 7 8 9 10 11 12 | $ cat qa.csv GENDER,ANSWER1,ANSWER2,ANSWER3 1,1,1,1 2,1,1,1 1,1,2,1 1,2,2,2 $ cat qa.csv | awk -F, '{print $3}' ANSWER2 1 1 2 2 | 
区切り文字には正規表現も使えますので、複数文字の区切り文字にも対応できます。
例:アクセスログから時刻だけ取得
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | $ cat access_log 192.168.62.58 - - [11/Nov/2012:00:00:05 +0900] "GET /rss/rss.rdf HTTP/1.1" 301 178 "-" "Jakarta Commons-HttpClient/3.0.1" "-" 0.000 192.168.62.58 - - [11/Nov/2012:00:00:05 +0900] "GET /rss/rss.rdf HTTP/1.1" 301 178 "-" "Jakarta Commons-HttpClient/3.0.1" "-" 0.000 192.168.62.58 - - [11/Nov/2012:00:00:06 +0900] "GET / HTTP/1.1" 200 21599 "-" "Jakarta Commons-HttpClient/3.0.1" "-" 0.024 192.168.62.58 - - [11/Nov/2012:00:00:06 +0900] "GET / HTTP/1.1" 200 21599 "-" "Jakarta Commons-HttpClient/3.0.1" "-" 0.028 192.168.35.9 - - [11/Nov/2012:00:00:16 +0900] "GET /rss/rss.rdf HTTP/1.0" 301 178 "-" "Jakarta Commons-HttpClient/3.0.1" "-" 0.000 192.168.35.9 - - [11/Nov/2012:00:00:16 +0900] "GET / HTTP/1.0" 200 21599 "-" "Jakarta Commons-HttpClient/3.0.1" "-" 0.029 $ cat access_log | awk -F'\\[|\\]' '{ print $2 }' 11/Nov/2012:00:00:05 +0900 11/Nov/2012:00:00:05 +0900 11/Nov/2012:00:00:06 +0900 11/Nov/2012:00:00:06 +0900 11/Nov/2012:00:00:16 +0900 11/Nov/2012:00:00:16 +0900 | 
ちょっとややこしい使い方
アクセスログから、日付、IP、UserAgentを取得したい!という時があります。
先ほどから度々登場しているaccess_logを使います。区切り文字に、カギ括弧にスペースを使えば問題なさそうですね。
| 1 2 3 4 5 6 7 | $ cat access_log | awk -F'\\[|\\]| ' '{print $4, $5, $1, $14, $15}'  11/Nov/2012:00:00:05 192.168.62.58 "Jakarta Commons-HttpClient/3.0.1"  11/Nov/2012:00:00:05 192.168.62.58 "Jakarta Commons-HttpClient/3.0.1"  11/Nov/2012:00:00:06 192.168.62.58 "Jakarta Commons-HttpClient/3.0.1"  11/Nov/2012:00:00:06 192.168.62.58 "Jakarta Commons-HttpClient/3.0.1"  11/Nov/2012:00:00:16 192.168.35.9 "Jakarta Commons-HttpClient/3.0.1"  11/Nov/2012:00:00:16 192.168.35.9 "Jakarta Commons-HttpClient/3.0.1" | 
上手く行ってそうですが、ところが…
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | $ cat access_log2 192.168.62.58 - - [01/Oct/2012:00:00:05 +0900] "GET /rss/rss.rdf HTTP/1.1" 301 178 "-" "Jakarta Commons-HttpClient/3.0.1" "-" 0.000 192.168.35.9 - - [01/Oct/2012:00:00:15 +0900] "GET /rss/rss.rdf HTTP/1.0" 301 178 "-" "Jakarta Commons-HttpClient/3.0.1" "-" 0.000 192.168.10.194 - - [01/Oct/2012:00:00:23 +0900] "GET /rss/rss.rdf HTTP/1.1" 301 178 "-" "Apple-PubSub/65.28" "-" 0.000 192.168.211.203 - - [01/Oct/2012:00:00:30 +0900] "GET /rss/rss.rdf HTTP/1.1" 301 178 "-" "Apple-PubSub/65.28" "-" 0.000 192.168.98.221 - - [01/Oct/2012:00:01:37 +0900] "GET /rss/rss.rdf HTTP/1.1" 301 178 "-" "livedoor FeedFetcher/0.01 (http://reader.livedoor.com/; 129 subscribers)" "-" 0.000 192.168.157.65 - - [01/Oct/2012:00:03:27 +0900] "GET /rss/rss.rdf HTTP/1.1" 304 0 "-" "Apple-PubSub/65.28" "-" 0.002 $ cat access_log2 | awk -F'\\[|\\]| ' '{print $4, $5, $1, $14, $15}'  01/Oct/2012:00:00:05 192.168.62.58 "Jakarta Commons-HttpClient/3.0.1"  01/Oct/2012:00:00:15 192.168.35.9 "Jakarta Commons-HttpClient/3.0.1"  01/Oct/2012:00:00:23 192.168.10.194 "Apple-PubSub/65.28" "-"  01/Oct/2012:00:00:30 192.168.211.203 "Apple-PubSub/65.28" "-"  01/Oct/2012:00:01:37 192.168.98.221 "livedoor FeedFetcher/0.01  01/Oct/2012:00:03:27 192.168.157.65 "Apple-PubSub/65.28" "-" | 
違う日のログでは上手く行きません。UserAgentは複数のスペースを含むので、フィールドの数が変わってしまい過不足が出てしまいます。スペースで区切るのが良くないようです。
行の中で出てくる固定の文字列としては、カギ括弧か、ダブルクオートです。ダブルクオートではIPが上手く区切れませんね。
こんな時には、awkのsplit関数を使いましょう!
カギ括弧とダブルクオートで分割すると、1つめのフィールドにIPなどが入ります。これをさらにスペースで分割するという方法です。
| 1 2 3 4 5 6 7 | $ cat access_log2 | awk -F'\\[|\\]|"' '{split($1, arr, " "); print $2, arr[1], $8}' 01/Oct/2012:00:00:05 +0900 192.168.62.58 Jakarta Commons-HttpClient/3.0.1 01/Oct/2012:00:00:15 +0900 192.168.35.9 Jakarta Commons-HttpClient/3.0.1 01/Oct/2012:00:00:23 +0900 192.168.10.194 Apple-PubSub/65.28 01/Oct/2012:00:00:30 +0900 192.168.211.203 Apple-PubSub/65.28 01/Oct/2012:00:01:37 +0900 192.168.98.221 livedoor FeedFetcher/0.01 (http://reader.livedoor.com/; 129 subscribers) 01/Oct/2012:00:03:27 +0900 192.168.157.65 Apple-PubSub/65.28 | 
先ほどとは出力が異なりますが、欲しい情報は取得できました!
awkが使いこなせるとログ調査とか捗りますので、日々精進したいですね。

 
						