grepの-oオプションと-Pオプションの組み合わせが便利
$ paste <(cat score | grep -oP '[^\d]+') <(cat score | grep -oP '\d+') | xargs -n 2 #シェル芸
— ぐれ (@grethlen) 2014, 6月 14
上記のようにgrepコマンドを叩いていたら「-oPってオプションなに?」と言われたので。
grep -oPって?
-oオプションとは
--only-matchingの略。マッチした部分のみを抽出するというオプションのこと。
$ echo "123abc456dfg" | grep -o [a-z] a b c d f g
-Pオプションとは
マッチさせる文字列にPerlで使われているものと同じ正規表現(Perl正規表現)をつかえるようにする。PerlのP。
普通の正規表現とPerl正規表現の違いって?
主に以下の3つ。
これらの特徴はPerl正規表現のみであり、 拡張正規表現オプション(-E)をつけたとしても 使うことはできない。
つまり-oPとすれば
$ echo "123abc456dfg" | grep -oP '\d' 1 2 3 4 5 6
どんな時に使える?
grepだけでテキストの「行の抽出」と「切り出し」が同時に行える。
具体例
たとえばこんなexample.xmlという名前のXMLファイルがあったとする。こいつからname要素内のテキスト、それも鍵カッコがついているもののみで、かつ(「」)で囲まれている中身だけを取り出したい。
「Cure Lovely」 「Cure Princess」 「Cure Honey」 「Cure Fortune」 Cure Gorilla
"Cure Gorilla"以外の下のような出力を得たい。さあ、どうする?
Cure Lovely Cure Princess Cure Honey Cure Fortune
- 安直に思いつく方法。
$ cat example.xml | grep '「' | sed 's/.*「//' | sed 's/」.*//g'
$ cat example.xml | xmllint --xpath '//name[contains(./text(),"「")]/text()' - | nkf --numchar-input | sed -r 's/(「|」)/\n/g'
- AWK先生
$ cat example.xml | awk -F'[「」]' 'NF>2{print $2}'
- sed先生
$ cat example.xml | sed -nr '/「/{s/.*「//g;s/」.*//gp}'
実はgrepだけでいける
$ cat example.xml | grep -oP '(?<=「).+(?=」)'
しくみ
Perl正規表現の「先読み・後読み」のパターンで文字列をマッチさせたとしても、 (?~)の中の文字列に関しては、grepの出力時にはマッチした文字列としてみなされない。試しに--colorオプションをつけても、「先読み・後読み」でマッチした部分は色は変化しない。
おそらくUNIXコマンド類の初学段階だと、awkやsedは文字列の「切り出し」、grepは「行の抽出」に使えるという認識になると思う。そして、学んでいくにつれてawkやsedもまた、「切り出し」のみならず「行の抽出」も同時にできるということがわかってくる。
しかし行の抽出にしか使えないと思われていたgrepも、実は-oPをつければ、文字列の「切り出し」にも使えますよというお話でした。