grepを使って正規表現を学ぶ

**grep** を題材に正規表現の説明を簡潔にまとめました


基本の基本しか扱わないので初心者向けの記事となります

とりあえず grep 以外の環境が揃えられない方向けにgithubにリポジトリを作成しました

[こちらです](https://github.com/aokiyuya/grep_test.git)

## 想定環境
- grep が使える環境ならなんでも
- UbuntuなどUNIXコマンドを使える環境(というかbash)

## 予備知識
所謂ワンライナーを多様します
何をしてるかと言いますと，
例えば
> ls | grep ほにゃらら

という文字列を何回も打ち込むことになりますが，このうち
ls は指定したディレクトリの中身を表示(標準出力)
| は標準出力を次のコマンドの標準入力にするというものです
grepは端的に言うと検索コマンドなので，ディレクトリの中身から特定のファイル名を抜き出すということを行っています

## 正規表現について
[「正規表現」『フリー百科事典　ウィキペディア日本語版』](https://ja.wikipedia.org/wiki/正規表現)によると
"正規表現（せいきひょうげん、英: regular expression）とは、文字列の集合を一つの文字列で表現する方法の一つである。正則表現（せいそくひょうげん）とも呼ばれ、形式言語理論の分野では比較的こちらの訳語の方が使われる。まれに正規式と呼ばれることもある。"
とある

雑に言うと，様々な文字列をとあるパターンに直そうという話です

例えば
A_000
A_001
A_002
…
A_999
という複数のファイルがあったとします

これらは一般化すると

A_(0~9の数字が三つ)

という風に表せます

書き方は様々あるとは思いますが，今回は grep を題材にお話をするので，以降は grep に限った話をします

## grep の基本
grep の由来は， UNIX の太古のエディタ ed のコマンド，g/re/p らしいです
grep は非常に早いコマンドで……と，grep について語っていると長くなってしまうのでさっさと本題に行きましょう

まず始めに grep が扱う正規表現を書き出します．

| 正規表現 | 意味 |
|:-----------:|:------------:|
| . | 改行文字以外の任意の1文字 |
| * | 直前の1文字の0回以上の繰り返しに一致 |
| ^ | 行頭 |
| $ | 行末 |
| [ ] | []内の任意の文字。-で範囲指定。[]内の最初の文字が^であればそれ以外という意味 |
| + | 直前の文字の1個以上の連続 |
| ? | 直前の文字の0または1文字にマッチ |
| pattern1&#x7C;pattern2 | pattern1あるいはpattern2のいずれかにマッチ |
| (pattern) | patternをグループ化 |
| \ | +や*を検索に使いたいときに(例:\\+ \\*) |

以下，問題のすぐ下に解答が乗っているので，ゆっくりスクロールすることをオススメします


### 実践編
まず，先に挙げた例のようなディレクトリを作ります
そのままでなくてもよいので，以下の様にターミナルに打ち込んでください

> mkdir grep_test
> cd grep_test
> touch A_{000..999}

この A_{000..999}というのは bash のブレース展開という機能です
とても便利なので愛用してます
ともあれ

> ls

と打ってみるとA_000~A_999までのファイルができてるはずです

あるいは

> ls | wc -l


で 1000 と表示されていれば大丈夫でしょう
ブレース展開のできないシェルを使っている場合は

> seq 0 999 | awk '{printf("touch A_%03d\n", $0)}' | sh

などとするのも良いと思います

では，まず始めにこの中から，

**A_000 ~ A_099**

を抜き出してください

やりかたは簡単で

> ls | grep A_0

とするだけ

すると，皆さんの画面にはA_000 ~ A_099が表示されたはずです

では，次の題です
二桁目(10の位)が6のファイルのみを抜き出して下さい

解答は

> ls | grep A_.6

これは，"." という文字が任意の1文字にマッチするため成立します
あるいは

> ls | grep A_[0-9]6

でもよいかもしれません
これは，"[0-9]"が0-9の数字1文字とマッチするため成立します

では次の題です

最初と同様に B_000 ~ B_999 を作成します
同様に，C_000 ~ C_999 も作成します
また，A_に関しては16進数で作りましょう

> ls | grep A_ | xargs rm
> touch A_{{0..9},{A..F}}{{0..9},{A..F}}{{0..9},{A..F}}

などと入力すれば十分なはずです

※rm は削除コマンドです．rm A_* でもいいのですが， A_ と * の間にスペースが入ったら悲惨なので(bashの全ファイル展開)，できるかぎり事前に消すファイルを表示してxargs(標準出力を引数に与えたコマンドの引数にするコマンド)でrmを使いましょう


まず始めに
上二桁が 99 あるいは下二桁が 99 のファイルを抜き出してください
また，下二桁が 99 のファイルのみも抜き出してください

解答は次の通りです
> ls | grep 99
> ls | grep 99$

$は行末を表す正規表現なので，99で終わるファイルのみを抜き出してくれます

ではさらにこの中で
999 より大きい数を抜き出してください

解答は次の通りです
> ls | grep 99 | grep -v [0-9]9[0-9]

ここでは grep の v オプション(検索に一致しないものを表示)を使っていますが，正規表現を使って

> ls | grep -e [A-F]99 -e 99[A-F]

あるいは
> ls | grep -e _[^0-9]99 -e 99[^0-9]

でも正解です
grep の -e オプションは明示的にパターンを指定する(通常は第一引数がパターンと見なされる)オプションで，複数指定してあげると OR 条件式のように働いてくれます
オプションを全く使わないのであれば

> ls | grep "\([A-F]99\)\|\(99[A-F]\)"


という解答でも良いと思います
これは，"()"でくくられたパターン二つのうちどちらかにマッチすれば出力ということですからOKです

以下は簡単なおさらいです
csvのデータにも同様にしてgrepを扱って色々なことができますよという話です

国民の祝日のcsvファイルを内閣府が配布してます
curl でとってきましょう

> curl http://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv | nkf > syukujitsu.csv

この中から，2017年の祝日を取り出してください

> cat syukujitsu.csv | grep 2017

次に，八月の祝日を取り出して下さい

> cat syukujitsu.csv | grep 20..-08

次に，20日以降の祝日だけ取り出して下さい

> cat syukujitsu.csv | grep 2.,

こんなところで，使い方はふんわりと分かったかなと思います
この程度の知識でもファイル整理(よく grep して xargs cp などしている)などには十分役立ちますから，覚えておくといいかもしれません

間違い等ありましたらお教えくださると幸いです