【Linux】grepと正規表現チートシート

#はじめに
どうも、未経験からエンジニア転職を目指しているもきおです。

今回はLinuxの<font color='red'> **grepと正規表現** </font>に触れていきたいと思います。
どちらも重要で共にLinux以外でも様々なプログラミング言語でも使われるので覚えておいて損はないと思いました。


今回の記事は新しいLinuxの教科書を参考にしています。
とてもLinux標準の教科書もやった事はありますが、よりわかりやすくまとまっているのでLinuxを学びたい方におすすめです！

https://www.amazon.co.jp/%E6%96%B0%E3%81%97%E3%81%84Linux%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8-%E4%B8%89%E5%AE%85-%E8%8B%B1%E6%98%8E/dp/4797380942/ref=asc_df_4797380942/?tag=jpgo-22&linkCode=df0&hvadid=295706574430&hvpos=&hvnetw=g&hvrand=13172813495091438571&hvpone=&hvptwo=&hvqmt=&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=1009244&hvtargid=pla-523780191776&psc=1&th=1&psc=1

#grepコマンド
grepコマンドは文字列を検索するコマンドです。
Ruby on Railsにおいてrails routes | grep [絞り込みたい文字列]みたいに使用した方もいるのではないでしょうか？
grepを使用する際の書式は以下となります。

書式：
grep 【オプション】<検索パターン> <ファイル名>

オプションには以下のようなものがあります。

|オプション|意味|
|----|----|
|-n |行番号付きで表示する|
|-i|大文字小文字の区別なく検索する|
|-v|指定した検索パターンを含まない行を検索する|

###使用例

```bash:grep使用例
#/etc/passwdファイルからbashという文字列を含む行を表示
$ grep bash /etc/passwd
root:x:0:0:root:/root:/bin/bash

#/etc/passwdファイルから-nを使用しbashという文字列を含む行を行番号付きで表示
$ grep -n bash /etc/passwd
12: root:x:0:0:root:/root:/bin/bash

#/etc/passwdファイルから-iを使用し大文字、小文字区別せずsystemという文字列を含む行を表示
$ grep -i system /etc/passwd
dbus:x:81:81:System message bus:/:/sbin/nologin
```

#正規表現

##そもそも正規表現ってなに？
プログラミングをやっていると時々耳にするこの「正規表現」、自分は最初たまに耳にするけど正規表現が何かよくわかってませんでした。

普段私たちが検索したいものは固定文字列ばかりではないと思います。例えば「Beer」という文字列だけでなく「Beeeer」という文字列も含めて検索したいとしましょう。「Beer」と検索しても「Beeeer」を検索一致させることは出来ません。

このような検索を行う際に、**条件文字列の集合を表現するための記法**が正規表現です。

##正規表現を使用する際の注意点
正規表現を使用する際は「' '」(シングルクォーテーション)でくくっておかないとシェルに正規表現が展開され自分の意図しない実行をされてしまう可能性があります。

例えば「ab*」で「ab」や「abbb」等を含めて検索したいとしましょう。
「' '」(シングルクォーテーション)でくくっておかないと以下のように解釈されてしまいます。

```bash:''で正規表現をくくらない場合
$ grep ab* sample.txt
##シェルが展開され実際に実行されてしまうコマンド
$ grep ab.txt ab.sh sample.txt
```


よって正規表現を使用する際は<font color='red'>**シングルクォーテーションでくくっておいて損はない**</font>でしょう。

##正規表現実用例
ここからは実際にgrepと正規表現を使用した例を紹介していきます。

###メタ文字って何？
正規表現では**「* {} $」**等の記号が特別な意味を持ちます。正規表現で意味を持つこのような記号をメタ文字と言います。

##文字にマッチするメタ文字
まず最初に任意の文字にマッチするメタ文字の正規表現を紹介します。

|メタ文字|意味|
|----|----|
|. |任意の1文字|
|[ ]|[]の中に含まれる、いずれかの1文字|
|[^ ]|[]の中に含まれない、いずれかの1文字|
|\|直後のメタ文字の意味を打ち消す|

###よく使いそうな使用例一覧

今回使用するファイルは以下

```bash:example.txt
test.example.net
tfst.example.net
tzst.example.net
tzzst.example.net
www.example.com
www.example.org
netapp.example.com
mail1.example.com
mail3.example.com
mail7.example.com
borg.example.com
```

```bash:.を使用し任意の1文字にマッチする検索
$ grep 't.st' example.txt
test.example.net
tfst.example.net
tzst.example.net
```
t.stで「tの後ろに任意の1文字が続き、その後ろにstという文字列がくる」というパターン検索をすることができます。

```bash:.を使用し任意の2文字にマッチする検索
$ grep 't..st' example.txt
tzzst.example.net
```
「..」と二つ続けると任意の2文字を指定することができます。

```bash:.(ドット)そのものを検索したい場合
$ grep '\.org' example.txt
www.example.org
```
.(ドット)そのものを検索したい場合は直前に\を置いて記載することで**メタ文字の意味を打ち消す**ことができます。このように特別な意味を持つ文字の意味を打ち消すことを<font color='red'>**「エスケープする」**</font>と言います。

```bash:[　]を使用し特定の文字を検索
$ grep 't[ef]st' example.txt
test.example.net
tfst.example.net
```
t[ef]stとするとtest、tfstの両方にマッチする検索ができます。

ここでよく使用されそうなのが[a-z A-Z]による全てのアルファベット大小関係なく検索する方法や[1-4]のように1,2,3,4が含まれる全ての文字列を検索する表記法です。


```bash:
#大小関係なく全てのアルファベットに一致するものを検索
$ grep 't[a-z A-Z]st' example.txt
test.example.net
tfst.example.net
tzst.example.net
#1-4の数字のみマッチするものを検索
$ grep 'mail[1-4]' example.txt
mail1.example.com
mail3.example.com
```

##位置にマッチするメタ文字
次に、位置にマッチするメタ文字の正規表現を紹介します。

|メタ文字|意味|
|----|----|
|^ |行頭を意味するメタ文字|
|$|行末を意味するメタ文字|

###実際の使用例
行頭がnetという文字列で始まる行を検索したいとしましょう。
単純にnetというパターンで検索すると以下のようになってしまいます。

```bash:netにマッチする検索
$ grep 'net' example.txt
test.example.net
tfst.example.net
tzst.example.net
tzzst.example.net
netapp.example.com
```
これだと行頭以外でもnetの文字列が含まれる行が検索されてしまいました。そこで行頭を意味する^を使用すると行頭でnetという文字列がある行だけ検索することができます。また行末の文字列を検索したい場合は文字列の末尾に$を使用することで可能です。

```bash:
#行頭のみマッチするものを検索
$ grep '^net' example.txt
netapp.example.com
#行末のみマッチするものを検索
$ grep 'net$' example.txt
test.example.net
tfst.example.net
tzst.example.net
```
##繰り返しを指定するメタ文字
最後に繰り返しを表すメタ文字の正規表現を紹介します。

|メタ文字|意味|
|----|----|
|*|0回以上の繰り返し|
|＼{m,n＼}|m回以上n回以下の繰り返し|
|＼{m＼}|ちょうどm回の繰り返し|
|＼{m,＼}|m回以上の繰り返し|
※全角のバックスラッシュじゃないとうまく表が作れなかったので全角にしていますが本来は半角のバックスラッシュです。

###よく使いそうな使用例一覧

今回使用する元ファイルは以下

```bash:drink.txt
Ber
Beer
BeerBeer
BeerBeerBeer
Beeeeeeer!!!
Bear
My Vodka
My Wine
Wine Wine
WineWine
```

###メタ文字*による繰り返し
\*を使用する事によって0回以上の繰り返しを表すことができます。
ここでの<font color='red'>0回とは、その直前の文字が存在しなくても検索条件に一致する</font>ということです。

```bash:*の利用例
$ grep 'Be*r' drink.txt
Ber
Beer
BeerBeer
BeerBeerBeer
Beeeeeeer!!!
Br
```
よって「Ber」、「Beeeeeer」だけでなく「Br」もマッチします。一方「Bear」にはマッチしません。

「Bear」を\*を使用し繰り返しによってマッチさせるには先程使用した[ ]を使用しB[ae]*rと記載することで検索することが可能です。

```bash:*の利用例
$ grep 'B[ae]*r' drink.txt
Ber
Beer
BeerBeer
BeerBeerBeer
Beeeeeeer!!!
Bear
Br
```

##あとがき

いかがだったでしょうか？
今回は今回はgrepと正規表現に触れました。

正規表現による組み合せの可能性は無限大で、Linux上級者の中では奇抜な正規表現を考えることを競い合ったりもしているらしいです。

ここら辺は今後かなり使えそうな分野なのでしっかりと理解を深めていきたいと思います！
最後までご覧いただきありがとうございました！！

# 参考記事
>[【sed / awk / grep】文字列の置換・抽出・検索と正規表現 | Linux Cheat Sheet]
(https://qiita.com/shuntaro_tamura/items/e4e942e7186934fae5e7)

>[grepコマンドの詳細まとめました【Linuxコマンド集】](https://eng-entrance.com/linux-command-grep)

>[【 grep 】コマンド――特定の文字を含む行を抽出する](http://www.atmarkit.co.jp/ait/articles/1604/07/news018.html)

>[【find・grep】特定の文字列を含むファイルのリストを取得する方法。](https://qiita.com/pokari_dz/items/0f14a21e3ca3df025d21)