grepと正規表現(regex)

##はじめに

この記事は、はてなダイアリーの同タイトルのものを、Qiitaのマークダウンの練習で移植したものです。:grinning:
http://d.hatena.ne.jp/namikawamisaki/20180206

（ここから）

いまさらこれやってもすでに、ぼうだいな量のドキュメントがあるので、何をいまさらという感じですが。自分の復習つうか自分が喋るならどうやってたかな（昔某所で研修してた気がする）というのを思い出す感じで書いてみます。

対象としては初学者。ほんとの基礎基礎はできてて、まだパイプとリダイレクトとかはいまいちわかってない人あたり。

- http://d.hatena.ne.jp/namikawamisaki/20160825
-- サーバ接続、対話型シェル
- http://d.hatena.ne.jp/namikawamisaki/20160827
-- ツリー構造、ファイル操作について


##今回のトピック

- 検索と「grepする」
- grepの使い方
- 正規表現とは（ちょっとPerlの世界寄り）
- マッチする（Perlの世界寄り）
- ワイルドカード #とは

##検索と「grepする」

「ググる」はもうGoogleで検索するをとびこえてYahoo!でも「ググる」っていう人がいるらしい。ウェブサイトから、何らかの質問の回答を得たいと思って、あるいは電車の時間を調べるために、ひとは検索する。

一方grep（ぐれっぷ）は、grep(1)[^1] には”print lines matching a pattern”とあり－－パターンマッチする「行」を「表示」する。



##grepの使い方

grepの基本的な使い方は、2種類になるかと思う。
1. ターゲットファイル内のパターンマッチ
2. コマンド実行結果のパターンマッチ

下記は1つ目の「ターゲットファイル内のパターンマッチ」について記した。ターゲットファイルの中身は、catというコマンドで表示している。



```
$ cat ./file1.txt
abcdefghijklmnopqrstuvwxyz
$ grep hoge ./file1.txt
$
```


（カレントディレクトリ内にある）test1.txt の内部に、「hoge」のパターンを含む行があるかをgrepコマンドで調べ、その結果何もなかったということを示している。

```
$ cat ./file1.txt
abcdefghijklmnopqrstuvwxyz
$ grep hijk ./file1.txt
$ abcdefghijklmnopqrstuvwxyz
$
```

hijkはfile1.txtに含まれているため、その行が表示される。（システムによってはhijkの部分に色がつくこともあるだろう。）


「コマンド実行結果のパターンマッチ」は、実務で利用頻度が上がるのではないだろうか。大量のログなどから目的のパターンにあう部分を抜き出す、といった作業でよく使っている。下記の例では、そのままRPMパッケージ数を出すと1484行出力されるが、kernelという文字を含むパッケージだけにしぼって表示している。

```
$ rpm -qa | wc -l
$ 1484
$ rpm -qa | grep kernel
kernel-xxxxxxxxx
kernel-xxxxxxxxx
kernel-devel-xxxxxxxxx
kernel-devel-xxxxxxxxx
kernel-headers-xxxxxxxxx
kernel-tools-xxxxxxxxx
kernel-tools-xxxxxxxxx
$
```



###オプション有りでの使い方

grepコマンドにはオプションがある。個人的に利用頻度が高いのは、`v`と`i`と`c`あたり。
くさるほど実例を書いたサイトがあるので、ここでは逆引き的にメモしておく。

| こうしたい時 | こうする |
|:------------|:--------|
| hogeって書いてる行は不要 | grep -v hoge ./targetfile.txt |
|hogeだけじゃなくてHogeもHOGEもとりあえず探す|grep -i hoge ./targetfile.txt|
|hogeって書いている行をカウントする|grep -c hoge ./targetfile.txt|
|hogeって書いていない行をカウントする|grep -cv hoge ./targetfile.txt|


##正規表現とは（ちょっとPerlの世界寄り）

※なんでPerl寄りって書いてるかというと私がPerlから正規表現にふれてたから

grepの後の文字列を、正規表現で記載することにより、さらに（柔軟に）ファイル等から文字列を探すことができる。


```
$ cat ./file1.txt
abcdefghijklmnopqrstuvwxyz
$ grep 'h.j' ./file1.txt
abcdefghijklmnopqrstuvwxyz
$
```

「h.j」は「h→何か1文字→j」を示す。よって、「h..j」では結果がないことになる。


```
$ cat ./file1.txt
abcdefghijklmnopqrstuvwxyz
$ grep 'h..j' ./file1.txt
$
```

正規表現を勉強するとなるとそれこそ、本にすればフクロウ本レベルになるし、ドキュメントを追うとものすごく広く深くはまっていく。はまるとおもしろい。そのへんは気に入った本やウェブサイトを見てもらうとして、文頭・文末の表現あたりは、見たことがなかったら今見ておいていいかなと思う。詳しくはregex(7)あたり。

- 文頭→^
- 文末→$

正規表現で`^hoge`と`hoge`は、「hogeからはじまるか」か、「hogeを含んでいるか」と意味が違ってくる。後者は「hogeからはじまる」も含まれることに注意。

`^`とか`$`は、「メタキャラクタ」といって正規表現で特別扱いされる。
regex(7)だと

- ^.[$()|#+?{\

で、Perlの世界では

- {}[]()^$.|#+?\

がそれに相当する。もし、そのものを文字として、パターンマッチをしたいときは、前に「半角の￥」を置く。

```
$ cat file2.txt
abc(def)ghi
$ grep ) ./file2.txt
Too many )'s.

$ grep \) ./file2.txt
abc(def)ghi
```


##マッチする（Perl寄り）

※なんでPerl寄りって書いてるかというと（略）

入門としては「一致する文字列を含む行を出す」くらいでいいと思う。もう一歩すすめるとしたならば、正規表現に「マッチ」しているのはどこか、ということを意識していく必要がある。


参考として、Perlのドキュメント、perlrequick[^2]に記載されている部分を引用する。

>
        "Hello World" =~ /o/;       # matches 'o' in 'Hello'
        "That hat is red" =~ /hat/; # matches 'hat' in 'That'


1行目は「oを含む行」で取り出そうとしている。この文章では2回oが出てきて、Perlの世界では先のHelloのoにマッチする。

2行目は、そのルールでいくとThatのhatにマッチということになる。


>
        "cats"          =~ /c|ca|cat|cats/; # matches "c"
        "cats"          =~ /cats|cat|ca|c/; # matches "cats"


これは「cats」という文字列についてだけど、正規表現としてはcでもcaでもcatでもcatsでもマッチする。書き方としては「最初に書いたやつ」が優先されるので、1行目の場合はcでマッチして、2行目の場合はcatsでマッチして、となる。


##ワイルドカード #とは

あとよく質問があるのが、「ワイルドカード」との違い。むかしは「また自分で調べておいてね」とかでぼんやりさせてしまってたけど、あらためて見直すと、もっと勉強しないとけないなと思った。のでここにはメモを置いておくので、あとはみなさんで。（結局なげるんかい）

- 正規表現では＊は「直前の文字の０回以上の繰り返し」[^3]
- ワイルドカードの＊は「任意の数の文字に一致」[^4]


```
$ ls
fil.txt  file.txt  file3.txt
$ ls | grep "file#"
fil.txt
file.txt
file3.txt
$ ls file#
file.txt  file3.txt
```

この例は、正規表現file#は

- filときて
- eが0回以上の繰り返し
- を含むもの
なので、fil.txtも、file.txtも、file3.txtも該当するし、
ワイルドカードのfile#は
- fileときて
- 任意の数の（文字数の）文字に一致

だから、fil.txtは含まれず、file.txtとfile3.txtが該当することになる。



##参考にしたもの

- LPICの教科書
-- 買い替えするときに[LinuCの話][Linuc]がきてしまってだな、、

[linuc]:https://linuc.org/

- 正規表現とワイルドカードは違います,http://blog.unfindable.net/archives/600
- サルにもわかる正規表現入門,http://www.mnet.ne.jp/~nakama/

[^1]: man 1 grep
[^2]: perldoc perlrequick または http://perldoc.jp/docs/perl/5.16.1/perlrequick.pod
[^3]: info ed
[^4]: https://technet.microsoft.com/ja-jp/library/cc483078.aspx