わんらいなーおさんぽ
お仕事中にとあるログのマッチパターンを作ろうとしたときに、特定箇所に現れる文字の種類を出そうとしてワンライナーをひねくりだしたときの思考回路を書いてみます。
シェルのいろんなコマンド覚えるとテキスト処理はほぼコマンドラインで完結できて楽よね。
ミッション
こんな感じのログがあります。
2015/02/23 17:00:01 [127.0.0.1] [info] [category] [ID1][AJ349_4gq4g0-][userID1] sample log1
2015/02/23 17:00:02 [127.0.0.1] [info] [category] [ID2][Iw3fajn_q2Q21/3f][userID2] sample log2
2015/02/23 17:01:21 [127.0.0.1] [info] [category] [ID19][q3jnm.z0933_q2-2124][userID19] sample log 3
2015/02/23 17:01:09 [127.0.0.1] [info] [category] [ID43][231kjHfuh3u8jq23][userID43] sample log 0004
IDとuserIDフィールドに挟まれた[]内に現れるランダムな文字列に現れる文字の種類を出しましょう。
たとえば一行目では[AJ349_4gq4g0-]の部分ね。
かんがえちゅう
まず該当部分の周りを抜き出す
いきなりスパッと出そうと思わないで、シェルはどれだけパイプをつないでも結局はシンプルに順番に処理するだけなので、落ち着いて少しずつ不要部分を切っていけばだいじょうぶ。
とりあえず空白で区切ってみよう。ホワイトスペースや特定文字をデリミタとして文字を分解するならawkがべんり。今回の対象部分の塊はスペースで区切って前から6個目にあるのでこんな感じ。
~$ cat sample.log | awk '{print $6}'
[ID1][AJ349_4gq4g0-][userID1]
[ID2][Iw3fajn_q2Q21/3f][userID2]
[ID19][q3jnm.z0933_q2-2124][userID19]
[ID43][231kjHfuh3u8jq23][userID43]
もうちょっと抜き出す
ココからさらに真ん中だけが欲しい。
よく見てみるとこの場合は、 ][ をデリミタにすればキレイにいけそうなので、今度はデリミタを指定する-Fオプションをつけてawkを追加。]と[はメタ文字なのでエスケープしてあげましょう。
~$ cat sample.log | awk '{print $6}' | awk -F"\]\[" '{print $2}'
AJ349_4gq4g0-
Iw3fajn_q2Q21/3f
q3jnm.z0933_q2-2124
231kjHfuh3u8jq23
分解する
なんとかして1文字1文字を出してuniqすればいいんですが、uniqは行単位なのでまず先に全ての文字を1行に1つずつ出してしまいましょう。
ここではsedを使って全ての文字の後ろに改行をつけてしまいます。
~$ cat sample.log | awk '{print $6}' | awk -F"\]\[" '{print $2}' | sed -E "s/(.)/\1\n/g"
A
J
3
4
9
_
... (略)
sed -E がうまく動かない(or そもそも無い)サーバではPerlのワンライナーあたりをこねくり回せばほぼ変わらない手間でサックリ書けます。
むしろ最初からPerl覚えちゃったほうがいろいろ楽かもしれないのは気にしてはいけない。
~$ cat sample.log | awk '{print $6}' | awk -F"\]\[" '{print $2}' | perl -lne 's/(.)/\1\n/g; print;'
最後に
コマンドラインでテキスト処理してるとだいたいお約束なのはsort + uniqですね。ついでに単品のxargsを添えれば見やすくなります
~$ cat sample.log | awk '{print $6}' | awk -F"\]\[" '{print $2}' | sed -E "s/(.)/\1\n/g" | sort | uniq | xargs
- . / 0 1 2 3 4 8 9 A H I J Q _ a f g h j k m n q u w z
結局何してたのか
sample.logなかんじのフォーマットのログをtd-agentでパターン書いて転送してたんですが、たまにログ抜けるなーと思っていたら、該当部分を \[\w*\]
っていうパターンで書いていたために、\w
でカバーできない文字が紛れ込んでいてマッチしなかったっていう事案でした。
ついでに、\wって[0-9a-zA-Z]だと思ってたけど地味にアンダースコアも含まれてるのね。
おさらい
- cat
- ファイル内容を出力するだけの存在。一応複数ファイルを渡すと結合できるという隠し機能がある。
- awk
- ワンライナーの強い味方。普段はホワイトスペース区切りの特定の位置を出すくらいだが、その力を理解し使役すればawkスクリプトという霊験あらたかなものを作ることができるようになる。
- sed
- テキスト置換の強い味方。便利だけど稀によくサーバにインストールされていない。覚醒させると"全ファイル内を捜索してすべてのマッチした部分を置換する"というマップ兵器が使えるがだいたい暴発してえらいことになる。
- perl
- 全てを統べる神。最近忘れ去られがち。
- sort / uniq
- 並べ替えて、重複を削除するというシンプルだが強力な一手。言うなれば魔神剣と虎牙破斬。
- xargs
- 敵の攻撃を吸収して撃ち出す必殺技。すべて合わせて一気に撃ち出すか一つ一つ撃ち出すかを選べるオプションも搭載されている。