正規表現を習得する方法としては、まず「基本正規表現」や「拡張正規表現」のルールを学び、それから使ってみる……というのが正攻法だと思う。だが、ルールばかり先に覚えようとしてもすぐに忘れるし、説明を読んだだけでは理解できないことも多い。実習が必要なのだ。
本連載では、「最小限のコストで最大の楽をする」というコンセプトの下、フィルタコマンドでよく使う正規表現のパターンをダイレクトに学んでいこう。「使えるサンプルをコピー&ペーストする中で正規表現を学ぶ」というアプローチだ。
ログファイルからIPアドレスだけを抜き出そう
サーバの運用管理をしていると、「ログファイルからアクセス元のIPアドレスだけを抜き出して集計したい」といったことがよくある。これをフィルタコマンドの「grep」で実行する方法を見ていこう。
まず、最も基本的な書き方をすれば以下のようになる。
「[0-9][0-9][0-9][.][0-9][0-9][0-9][.][0-9][0-9][0-9][.][0-9][0-9][0-9]」が、パターンだ。これは、前回までに説明した方法だけで書いている。
後から見直した際にわかりやすい点は良いのだが、ちょっとばかり長い。そこで、繰り返し指定を使うともうちょっとパターンを短くすることができる。こんな感じだ。
「{3}」という表記が繰り返し指定で、「この表記の前の文字、またはパターンを3回繰り返す」という意味になる。ただし、この指定方法だとIPアドレスはそれぞれの桁が必ず3桁でなければならないので、例えば「128.0.0.0」のようなIPアドレスは拾うことができない。
これをどう解消するか。実は、繰り返し指定は「{3}」だと3回繰り返しに固定されるが、「{1,3}」のように書くと「1回~3回の繰り返しに一致する場合」という意味になる。したがって、次のようにパターンを指定すれば、桁が3桁だけではないIPアドレスにも一致するようになる。
もっと緩く指定したい
また、もっと緩めに指定することもできる。「{1,3}」は「1回~3回の繰り返し」という指定だが、「+」を使うと「1回以上の繰り返し」という意味になる。これを使うと、次のような表記になる。
この表記ではIPアドレス以外のパターンも拾ってしまうが、短くてわかりやすいし、大体の仕事はこれでこなせるので、この書き方を覚えておくと応用が効くだろう。
さらに緩い指定をするなら「*」を使う方法がある。これは「0回以上の一致」に相当する。つまり、「1つも一致しないか、または1回以上」といった意味になる。次のように緩めに拾ってしまうのだが、これも目的によってはそれなりに利用できる。
厳密に指定してみる
逆に、もっと厳密にIPアドレスだけを拾ってくるようにパターンを指定するような書き方もできる。パターンをグルーピングする「()」と、条件のOR指定を指定する「|」を組み合わせて次のように書ける。
ここではさらに、一致した部分だけを抜き出す-oオプションを指定することで、対象となるログファイルからIPアドレスだけを抜き出すようにしている。
「(^|[^0-9]+)([0-9][.]|[1-9][0-9][.]|[12][0-9][0-9][.]){3}([0-9]|[1-9][0-9]|[12][0-9][0-9])($|[^0-9])」の部分がパターンの記述だが、ここまでくるとそろそろ解読が辛くなってくる。行頭、行末、デリミタを意識しつつ、IPアドレス(0~255.0~255.0~255.0~255)の指定以外のパターンには一致させないという指定だ。ただし、こうした厳密な書き方は、後から見直すと何を指定したのかわからなくなっていることが多い。「こういったこともできる」程度に覚えておけばよいと思う。
いろいろな表記方法を紹介したが、いちばんお薦めの書き方は「[0-9]+[.][0-9]+[.][0-9]+[.][0-9]」だ。この書き方で十分に目的は果たせるし、短くてわかりやすい。「呪文」は難しくしすぎないことが、後から自分の書いたスクリプトやコマンドを理解する上で大切なのだ。