share facebook facebook twitter menu hatena pocket slack

2011.11.24 THU

シェルってなんじゃ?(sedとsortでアクセスログのソート)

三浦 悟

WRITTEN BY 三浦 悟

Apache等のアクセスログに生成される、複数ホストで同一日付のファイルをAWSTATSなどで使用する場合があります。
その場合logresolvemergeなどを利用するのですが、logresolvemergeは各ファイルが内部でソートされていることを前提としています。
ログは書きこみプロセスなどの関係で時折内部で日時が前後することがあるため、ファイルの内部をソートし直す必要があります。

スクリプト言語等のループ等を利用してもソートすることができるのですが、今回はシェルを使った方法を紹介したいと思います。

使うコマンドは下記になります。

  • cat
  • sort
  • sed

例として、ログの中身が下記のようになっているファイルが数十個あるとします。

125.54.146.207 – app [21/Nov/2011:15:30:28 +0900] “GET /hoge.js HTTP/1.1” 304 –
125.54.146.207 – app [21/Nov/2011:15:30:28 +0900] “GET /moge.js HTTP/1.1” 304 –
125.54.146.207 – app [21/Nov/2011:15:29:01 +0900] “GET / HTTP/1.1” 304 –
125.54.146.207 – app [21/Nov/2011:15:30:26 +0900] “GET /hoge.php HTTP/1.1” 200 13827
125.54.146.207 – app [21/Nov/2011:15:30:28 +0900] “GET / HTTP/1.1” 304 –

ここでは、hoge1.log, hoge2.logの以下の2ファイルをサンプルとして使用します。

$ cat hoge1.log
125.54.146.207 - app [21/Nov/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Dec/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [05/Nov/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [18/Apr/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [22/Nov/2001:15:30:28 +0900] "GET / HTTP/1.1" 304 -

$ cat hoge2.log
125.54.146.207 - app [27/May/2011:16:34:01 +0900] "GET /change/hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:11:22 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [01/Nov/2010:05:29:01 +0900] "GET /moge/ HTTP/1.1" 304 -
125.54.146.207 - app [13/Oct/2011:13:08:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [11/Mar/2011:14:54:28 +0900] "GET / HTTP/1.1" 304 -

○cat

はじめに、これらのファイルを連結します

$ cat *
125.54.146.207 - app [21/Nov/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Dec/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [05/Nov/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [18/Apr/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [22/Nov/2001:15:30:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [27/May/2011:16:34:01 +0900] "GET /change/hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:11:22 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [01/Nov/2010:05:29:01 +0900] "GET /moge/ HTTP/1.1" 304 -
125.54.146.207 - app [13/Oct/2011:13:08:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [11/Mar/2011:14:54:28 +0900] "GET / HTTP/1.1" 304 -

連結しただけなのでまだ順番は変わりません。

次に、その出力を普通にソートしてみます。
sortコマンドには以下のようにセパレータで区切られたフィールドと範囲指定でソートキーを優先順に複数定義することができる-kオプションがあります。

以下は、区切り文字を半角スペースとして1行をフィールド分割したときの2番目のフィールドの2文字目から3文字目を第1ソートキー、1番目のフィールドを第2ソートキーとすることを表します。

$ cat test.log
b A03z 2
a A12f 3
a B03d 1

$ sort -t " " -k 2.2,2.3 -k 1,1 test.log
a B03d 1
b A03z 2
a A12f 3

これを利用して以下のログファイルをソートしてみます。

$ cat hoge1.log
125.54.146.207 - app [21/Nov/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Dec/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [05/Nov/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [18/Apr/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [22/Nov/2001:15:30:28 +0900] "GET / HTTP/1.1" 304 -

$ cat hoge2.log
125.54.146.207 - app [27/May/2011:16:34:01 +0900] "GET /change/hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:11:22 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [01/Nov/2010:05:29:01 +0900] "GET /moge/ HTTP/1.1" 304 -
125.54.146.207 - app [13/Oct/2011:13:08:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [11/Mar/2011:14:54:28 +0900] "GET / HTTP/1.1" 304 -

$ cat * | sort -t " " -k 4.9,4.12 -k 4.5,4.7 -k 4.2,4.3 -k 4.14,4.15 -k 4.17,4.18 -k 4.20,4.21
125.54.146.207 - app [22/Nov/2001:15:30:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [01/Nov/2010:05:29:01 +0900] "GET /moge/ HTTP/1.1" 304 -
125.54.146.207 - app [18/Apr/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [21/Dec/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [11/Mar/2011:14:54:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [27/May/2011:16:34:01 +0900] "GET /change/hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [05/Nov/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:11:22 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [13/Oct/2011:13:08:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827

期待通りのソートを行うことができませんでした。
これはログを出力したマシンのロケールの問題で、月のフィールドが3文字コードになっているため、文字列では大小を比較することができませんでした。

このような場合のために、sortには-Mオプションが用意されており、

Jan $ cat * | sed -e "s:/Jan/:/01/:" -e "s:/Feb/:/02/:" -e "s:/Mar/:/03/:" -e "s:/Apr/:/04/:" -e "s:/May/:/05/:" -e "s:/Jun/:/06/:" -e "s:/Jul/:/07/:" -e "s:/Aug/:/08/:" -e "s:/Sep/:/09/:" -e "s:/Oct/:/10/:" -e "s:/Nov/:/11/:" -e "s:/Dec/:/12/:"

125.54.146.207 - app [21/11/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/12/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [05/11/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [18/04/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [22/11/2001:15:30:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [27/05/2011:16:34:01 +0900] "GET /change/hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/11/2011:15:11:22 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [01/11/2010:05:29:01 +0900] "GET /moge/ HTTP/1.1" 304 -
125.54.146.207 - app [13/10/2011:13:08:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [11/03/2011:14:54:28 +0900] "GET / HTTP/1.1" 304 -

sedでは上記のように-eオプションを連結することで、個々の置換条件を複数適用することができます。
また、パターンの最初のsの直後をセパレータとみなすので、/以外でもセパレータとして使用することができ、パターン内に/がある場合などに便利です。

○sort

それでは、この結果をソートにかけるようにパイプで連結します。

$ cat * | sed -e "s:/Jan/:/01/:" -e "s:/Feb/:/02/:" -e "s:/Mar/:/03/:" -e "s:/Apr/:/04/:" -e "s:/May/:/05/:" -e "s:/Jun/:/06/:" -e "s:/Jul/:/07/:" -e "s:/Aug/:/08/:" -e "s:/Sep/:/09/:" -e "s:/Oct/:/10/:" -e "s:/Nov/:/11/:" -e "s:/Dec/:/12/:" | sort -t " " -k 4.8,4.11 -k 4.5,4.6 -k 4.2,4.3 -k 4.13,4.14 -k 4.16,4.17 -k 4.19,4.20
125.54.146.207 - app [22/11/2001:15:30:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [01/11/2010:05:29:01 +0900] "GET /moge/ HTTP/1.1" 304 -
125.54.146.207 - app [11/03/2011:14:54:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [18/04/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [27/05/2011:16:34:01 +0900] "GET /change/hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [13/10/2011:13:08:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [05/11/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [21/11/2011:15:11:22 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/11/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/12/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -

期待通りにソートを行うことができました。

○sed

上記のままでもいいのですが、月がソート用に数字変換されたままですので、最後に元に戻します。

$ cat * | sed -e "s:/Jan/:/01/:" -e "s:/Feb/:/02/:" -e "s:/Mar/:/03/:" -e "s:/Apr/:/04/:" -e "s:/May/:/05/:" -e "s:/Jun/:/06/:" -e "s:/Jul/:/07/:" -e "s:/Aug/:/08/:" -e "s:/Sep/:/09/:" -e "s:/Oct/:/10/:" -e "s:/Nov/:/11/:" -e "s:/Dec/:/12/:" | sort -t " " -k 4.8,4.11 -k 4.5,4.6 -k 4.2,4.3 -k 4.13,4.14 -k 4.16,4.17 -k 4.19,4.20 | sed -e "s:/01/:/Jan/:" -e "s:/02/:/Feb/:" -e "s:/03/:/Mar/:" -e "s:/04/:/Apr/:" -e "s:/05/:/Apr/:" -e "s:/06/:/Jun/:" -e "s:/07/:/Jul/:" -e "s:/08/:/Aug/:" -e "s:/09/:/Sep/:" -e "s:/10/:/Oct/:" -e "s:/11/:/Nov/:" -e "s:/12/:/Dec/:"
125.54.146.207 - app [22/Nov/2001:15:30:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [01/Nov/2010:05:29:01 +0900] "GET /moge/ HTTP/1.1" 304 -
125.54.146.207 - app [11/Mar/2011:14:54:28 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [18/Apr/2011:15:30:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [27/Apr/2011:16:34:01 +0900] "GET /change/hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [13/Oct/2011:13:08:26 +0900] "GET /hoge.php HTTP/1.1" 200 13827
125.54.146.207 - app [05/Nov/2011:15:29:01 +0900] "GET / HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:11:22 +0900] "GET /moge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Nov/2011:15:30:28 +0900] "GET /hoge.js HTTP/1.1" 304 -
125.54.146.207 - app [21/Dec/2011:15:30:28 +0900] "GET /moge.js HTTP/1.1" 304 -

こちらの記事はなかの人(memorycraft)監修のもと掲載しています。
元記事は、こちら

三浦 悟

三浦 悟

高円寺在住のなんじゃ系男子 またの名をmemorycraftといいます。 炭水化物大好き 日々の「なんじゃ?」を記事にしてます。

cloudpack

cloudpackは、Amazon EC2やAmazon S3をはじめとするAWSの各種プロダクトを利用する際の、導入・設計から運用保守を含んだフルマネージドのサービスを提供し、バックアップや24時間365日の監視/障害対応、技術的な問い合わせに対するサポートなどを行っております。
AWS上のインフラ構築およびAWSを活用したシステム開発など、案件のご相談はcloudpack.jpよりご連絡ください。