Help us understand the problem. What is going on with this article?

【正規表現】個人的備忘

More than 1 year has passed since last update.

正規表現を未だに使いこなせず、ちょっとしたマッチ処理も度々苦戦するので、ノウハウを蓄積するためにまとめておく。

※表記の誤りや、もっとスマートに表現できる等のアドバイス等ございましたら、ご遠慮なくご指摘頂ければ幸いです。

1. 最後に出現する指定文字までマッチ

ex) /hoge/fuga/foobar.txtというパスについて、ファイル名のパスまで取得する。

【考え方】
最初の文字から最後のスラッシュをマッチングさせて抽出させれば良い。

echo /hoge/fuga/foobar.txt | grep -oe ".*\/" # /hoge/fuga/

【解説】
grepの-oオプションはonly matching(一致した部分のみ抽出)の意味。-eは正規表現使用のオプション。
.*で0文字以上の任意の文字列を表現。最後に\/(\はエスケープ文字)でスラッシュまでマッチ。

2. 最初に出現する指定文字までマッチ

ex) aaa/bbb/ccc/dddという文字列について最初の/(スラッシュ)まで抽出したい。

【考え方】
事例1のパターンでは最長一致でマッチさせているため、最後のスラッシュまで抽出してしまう。
今回のパターンだと、最短一致によって最初に出現したスラッシュまでを抽出したいが、grepで最短一致を使用するにはテクニックが必要。というより対応していないらしいので、-Pオプションの使用によりPerl正規表現を有効にして最短一致を表現する。

echo aaa/bbb/ccc/ddd | grep -oP "^.*?\/" #aaa/

【解説】
^は先頭文字の意味。これをつけないとaaa/ , bbb/ , ccc/の3つがマッチしてしまう。?\/の表現は、?の直後の文字について、最短で出現した文字をマッチさせる。すなわち、aaaの直後のスラッシュが最短一致する。

3. sedによる後方参照(これ便利) ※2018/10/20修正

ログ解析をするときに、特定の値をマッチさせて後処理で演算を行いたいシュチュエーションがある。
例えば下記のようなGCログがあったとする。このログからFullGC直後のメモリ使用量(下記例の1行目で言うと、FullGC: 3328K->384K(3712K)の384K)の数字384だけを抽出することを考える。

[GC 3.125: [FullGC: 3328K->384K(3712K), 0.0112500 secs] 8168K->5998K(16000K), 0.0113291 secs]
[GC 4.539: [FullGC: 3712K->4K(3712K), 0.0032661 secs] 9326K->5999K(16000K), 0.0033515 secs]
[GC 10.669: [DefNew: 3332K->3K(3712K), 0.0015267 secs] 9327K->5999K(16000K), 0.0015929 secs]
[GC 12.103: [DefNew: 3331K->3K(3712K), 0.0003727 secs] 9327K->5999K(16000K), 0.0004336 secs]
[GC 13.539: [DefNew: 3331K->4K(3712K), 0.0003766 secs] 9327K->5999K(16000K), 0.0004347 secs]
[GC 18.199: [FullGC: 3332K->10K(3712K), 0.0012516 secs] 9327K->6005K(16000K), 0.0013153 secs]

私の従来のやりかただと、例えば抽出したい数値のの前後に記号があるとき(ex: <12345>)や特定のキーワードが数値の前にあるとき(ex: Total:12345)はその特定の記号やキーワードをトリガーにして下記のようにマッチ条件を書いていた。

echo "foo123<12345>123bar" | grep -oe \<[0-9]*\> # <12345>

当然だが、これだと抽出したい数値の前後にトリガー文字が必然的についてきてしまう。
数値だけを抽出したい場合はどうすれば良いのだろうか。

※2018/10/20修正
そこで便利だと感じたのがsedで後方参照を使用する方法である。後方参照とは、マッチした部分文字列を後で再利用することができる機能である。後方参照させたい部分は( ) で囲んで表現し、参照時は \1, \2,,,,とし、何番目の部分文字列を参照したいかを指定することができる。

後方参照の定義の理解に誤りがございました。
パターンにマッチし記憶された部分文字列をパターン内でも参照することが可能 な機能が本来の使われ方となります。
https://www.javadrive.jp/perlregular/ref/index5.html

従って下記の例において、置換前に()で囲み、置換後パターンに後方参照\1,\2,,,として参照して文字列を取り出すということは、本来の後方参照の用途からは逸れるものとなります。
norisuke3@githubさん、ご指摘ありがとうございました。
※修正ここまで

sedは置換機能に優れており、基本的な使い方は次の通り。

sed -r 's/置換前文字/置換後文字/' # -rは拡張正規表現

# ex)
echo hogefuga | sed -r 's/hoge/fuga/' # fugafuga

以上を踏まえると、抽出したい文字列を自由自在に操ることができる。
例えば123<999>456<888>789という文字列から< >内の999, 888だけを抽出する場合は次のように書ける。

echo "123<999>456<888>789" | sed -r 's/^.*<([0-9]*)>.*<([0-9]*)>.*$/\1 \2/' # 999 888

上記の例では抽出したい数値以外の任意文字列は.*で一括マッチさせ、トリガー文字列(この場合は<>)は明示的に記述している。抽出したい文字列は数値であるから後方参照の括弧で囲い、([0-9]*)とした。そして、最後に抽出したい数値を後方参照\1 \2で取り出している。

最後にこれらの基本を押さえると最初のGCログの例ではFullGCの数値は下記のように抽出できる。

cat GClog | grep FullGC | sed -r 's/^.*FullGC:\s[0-9]*K->([0-9]*)K.*$/\1/'
384
4
10

【参考】上記の演算結果で最大値を取得したいときはawkで下記のように書ける。よく使うが忘れるのでメモ。

| awk '{if(max<$1) max=$1} END{print max}'
kod314
インフラエンジニア。趣味で機械学習をお勉強中。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away