ログの整理

シェルスクリプト総合 その14 からの問題を取り上げます。

質問です。

以下のような形式のログ(MySQLのバイナリログみたいなもの)があるとします。
---------------------
09/04/01 12:00 aaa
09/04/01 13:10 aaa
               bbb
09/04/01 13:20 ccc
09/04/01 14:40 aaa
               ddd
               eee
               ccc
---------------------
このような形式のログから、例えば、13時台のログを抜き出すにはどうしたらいいでしょうか?
上記のログでいうと、以下のような結果がほしいです。
---------------------
09/04/01 13:10 aaa
               bbb
09/04/01 13:20 ccc
---------------------
awkとgrepを駆使すればできそうなのですが、いまいちわかりません。
よろしくご教示お願いします。

awk はログの整理に非常に役に立つ言語ですが、できるわけ分かりやすく、awk らしく記述することが重要です。 しばしば、一行野郎で記述してしまいますが、awk が不得意な人が後でメンテナンスを行う場合には解読するのに時間がかかることがあります。 ここでは少し分かりやすく記述しておきます。

#! /usr/bin/gawk -f
# log_13.awk

# フィールドが 3 つで 13 時台の時
#   13 時台: 2 番目のフィールドの 1 文字目から 2 文字目の文字列が 13 である
NF == 3 && substr($2, 1, 2) == 13 {
    flag = 1;
    print $0;
}

# フィールドが 3 つだが 13 時台でない時
#   13 時台: 2 番目のフィールドの 1 文字目から 2 文字目の文字列が 13 でない
NF == 3 && substr($2, 1, 2) != 13 {
    flag = 0;
}

# フィールドが 1 つでフラグが真である時
NF == 1 && flag == 1 {
    print $0;
}

上のスクリプトを見れば awk を知らなくても何となく分かるのではないでしょうか? また、パターン + アクションの awk らしい記述ですから、awk を知っていれば一目瞭然です。 実行してみましょう。

$ nawk -f log_13.awk log_13.in
09/04/01 13:10 aaa
               bbb
09/04/01 13:20 ccc

さて、こうした awk スクリプトは「13 時」という決め打ちを除外して変数にしておくと使い回すことができます。

#! /usr/bin/gawk -f
# log.awk

NF == 3 && substr($2, 1, 2) == hour {
    flag = 1;
    print $0;
}

NF == 3 && substr($2, 1, 2) != hour {
    flag = 0;
}

NF == 1 && flag == 1 {
    print $0;
}

ただし、このままでは変数 hour には値が入っていませんので、awk のオプションとして与えます。

$ nawk -v hour=12 -f log.awk log_13.in
09/04/01 12:00 aaa

$ nawk -v hour=13 -f log.awk log_13.in
09/04/01 13:10 aaa
               bbb
09/04/01 13:20 ccc

$ nawk -v hour=14 -f log.awk log_13.in
09/04/01 14:40 aaa
               ddd
               eee
               ccc

こうすることで一気に自由度が増えます。

個人的な意見ですが、awk にオプションで変数を指定するスクリプトを作ると、どういうオプションが与えられなければ正常に動作しないか分かりにくいことが多くあります。 その場合には、オプションを記述した Makefile や shell スクリプトでラップすれば少しは分かりやすくなるかもしれません。

tag_nawk.pngtag_nawk.pngtag_nawk.pngtag_nawk.png