今回はUNIX系では定番のgrepです。grepはテキスト検索を行いますが、なんと言っても強力な正規表現が使えるのが大きなポイントです。
似たような単語、数字、パターンを持つ文字列の検索に大きな威力を発揮します。もちろん正規表現を上手に使えれば、です。それでも、そこそこ正規表現が使えるだけでも十分ではないかと思います。だいたい、この前振りからして書いてる本人が、正規表現に精通してるわけでもなく、正規表現はそこそこという具合なので、そのパターンマッチよろしくないかも、というオチがあるかもしれません。特にgrepは過去の経緯もあり挙動が異なる場合があります。ここまでの前振りで何か難しそうだなと思う人もいるでしょう。でも、何となく動けば用が足りることもあります。何となく動けばOK!というのが、この連載なので何かうまく検索できなかった場合は調べて勉強するのもよいかもしれません。おまけに今はChatGPTなどのAIがアシストしてくれます。

さて、今回もデスクトップのsampleディレクトリにサンプルとなるテキストファイルを入れておきます。また、カレントディレクトリはsampleディレクトリとします。コマンドならcd ~/Desktop/sampleです。


○正規表現とgrep

grepを使いこなすのは正規表現を使いこなすような一面もあり難しいところもあります。そもそも、正規表現自体が難しい部分もありますし、正規表現についてだけで一冊の本が出ているほどです。

おおよそ1990年代以降のプログラム言語やアプリケーションには標準で正規表現が利用できるものがあります。中にはプログラムで正規表現を使う人もいるでしょう。その場合、そこで培った正規表現の知識は活用できます。すでにそのような知識やノウハウを持っている人なら容易にgrepを使いこなせるかもしれません。

でも、まったく正規表現なんて使った事がないという人もいるでしょう。私もそんなに多用するわけではなく必要に応じて使う程度です。

正規表現は難しいと言う場合は人工知能(AI)サービスを利用する方法もあります。例えばChatGPT/Geminiだと以下のようにすると正規表現例を提示してくれます。grepの単語も指定するとオプション付きで提示してくれます。これは便利でありがたいサービスと言えるでしょう。
なお、AIやAIのモデルによって回答が異なることがあります。

【grepで西暦4桁と2桁の月にマッチする正規表現を教えてください。】grep -E '[0-9]{4}-(0[1-9]|1[0-2])' file.txtgrep \b[0-9]{4}-(0[1-9]|1[0-2])\b date.txt

20世紀と違い今では正規表現を駆使するよりもプログラム言語を利用した方がいい場合もあります。昔と違ってメモリやディスク容量、処理速度などの制約も少なくなったのでgrepも適材適所で使えばよいでしょう。また、上記のようにAIを活用するのが21世紀のやり方かもしれません。
○正規表現なしで検索

 まずは正規表現なしでgrep検索してみましょう。grepと言えども正規表現を使わなければ難しくない、という事です。正規表現でなければ単純な部分一致検索になります。
 なお、今回は日本語は扱わずに純粋に英数字と記号のみとしています。
 grepはファイル名(ファイルパス)を指定して検索するだけでなく、標準入力からの文字列を検索することもできます。grepが便利で多用されるのは標準入力からのデータを扱えるからという理由もあるでしょう。
 まず、カレントディレクトリにあるテキストファイルのsample1.txtの内容を検索してみましょう。
sample1.txtの内容は以下のようになっています。1行目が日付等のヘッダーになっていて、2行目以降が3列でタブ区切りになっているデータです。

sample1.txt(※表示ではスペースになっていますが、実際はタブ区切りです。viでは:%s/ /\t/gで置換できます)

test data 2022/02/22abTest15$1.2abTest245$1.1abTest398$3abtest43$0.7abtest514$0.35bbTest06100\1bbTest07200\2cbTest08400\4cbTest09999\9

 最初に検索する文字はaの一文字です。以下のようにするとカレントディレクトリにあるsample.txtファイル内にある文字列からaの文字を検索します。ここでaの文字を囲む記号は'(シングルクオーテーション)です。以下の例では"(ダブルクオーテーション)で囲んでも結果は同じです。"(ダブルクオーテーション)は文字列内に記号が含まれる場合に動作が異なります。これについては後ほど説明します。
以下のコマンドを実行すると一致する文字がある行が出力されます。grepでの検索は行単位で処理されます。

grep 'a' sample1.txt

一致する文字がない場合は何も出力されません。


grep 'p' sample1.txt

一文字の検索ではなく複数の文字の検索もできます。当然と言えば当然ですが、検索する文字によっては注意しないといけない事もあります。
まずは、なんの問題もない複数文字の検索をしてみましょう。もちろん問題が発生しない英数字のみの組み合わせです。以下の例ではカレントディレクトリにあるsample1.txtファイルから、abTest1の文字列を検索し結果を表示します。

grep 'abTest1' sample1.txt

abTest1の文字列が含まれるのは一行しかありませんので期待通りの結果になっています。それでは次にテキストファイル内にある半角バックスラッシュを検索してみましょう。

grep '\' sample1.txt

コマンドを入力すると以下のようなエラーが表示されてしまい検索が実行されません。

grep: Trailing backslash 【Linux/Ubuntu】grep: trailing backslash (\) 【macOS】

何がいけなかったのかというと、バックスラッシュは後に続く文字をエスケープするために使われる記号だからです。このため、文字列内にあるバックスラッシュを検索する場合は以下のようにバックスラッシュを2つ連続して書く必要があります。

grep '\\' sample1.txt

今度はバックスラッシュが含まれる行が出力されました。一部の記号($や\)にはバックスラッシュをつける必要がありますが、そのような記号が出てくるたびにバックスラッシュを付けていたら読みにくくなってしまいます。
そんな時に便利なFオプションがあります。以下のように純粋に検索する文字を指定するだけです。

grep -F '\' sample1.txt

結果は期待通りになっています。ここまでは正規表現なしでの検索です。検索文字列を変えていろいろ検索してみてください。ただし、検索文字は"(ダブルクオーテーション)で囲まないでください。"(ダブルクオーテーション)で囲むと指定する文字によって動作が変わってしまうからです(これはシェルによってgrepに渡す前に文字列が処理されてしまうため)。例えば$を検索する以下のようなパターンです。

grep "\\\$" sample1.txt ↓(bash,zshで"~"内が処理され以下のように変換)grep \$ sample1.txt ↓\$はgrepでは$の文字単体として検索される

○完全一致検索

 部分一致検索ができるなら完全一致検索もしたいと考えるのは当然です。完全一致検索の場合、正規表現を利用することになりますが、簡単なので先にやってしまいましょう。やるだけで説明は後回しにします。
grepの完全一致検索は行に対して行いますので行頭から行末まで一致させるように指定します。

ここでは以下の行の内容に完全に一致させるように検索してみましょう。

abTest2 45 $1.1

この場合、先頭から行末までなので先頭に^記号、末尾に$を指定します。なお、検索する文字列内に$など正規表現で使うメタ文字が含まれている場合はバックスラッシュでエスケープさせる必要があります。
また、文字列内にタブコードが含まれている場合も\tのようにエスケープさせる必要があります。
つまり、以下のように指定すれば完全一致検索になります。macOSの場合は-Pを指定しなくてもマッチしますが、Linuxでは-Pを指定しないとマッチしません。

grep -P '^abTest2\t45\t\$1\.1$' sample1.txt

○正規表現を使って検索

 それでは正規表現を使って文字列を検索してみましょう。今では多くのプログラム言語で使える正規表現ですが、歴史的な都合でgrepには基本正規表現と拡張正規表現、Perl拡張表現があります。現在のプログラミング言語等に慣れていて正規表現を使う場合は拡張正規表現のオプションであるEを指定してgrepを使ったほうが無難かもしれません。
 今度はsample1.txtに行を追加して以下のようにしたサンプルテキストを使います。このファイルはsample2.txtという名前になっています。

sample2.txt(※表示ではスペースになっていますが、実際はタブ区切りです。viでは:%s/ /\t/gで置換できます)

test data 2022/2/22abTest01 50 $1.2abTest02 45 $1.1abTest03 98 $3abtest04 30 $0.7abtest05 14 $0.35bbTest06 100 \1bbTest07 200 \2cbTest08 400 \4cbTest09 999 \9ddTest01 1024 12yenddTest02 4096 240yenddTest02 256 240yenyenTest01 987 128yenTest02 215 880yenyenTest02 314 128YenyenTest03 2718 128YEN

 正規表現が便利なのは3桁の数字だけ検索する、特定の文字で始まり特定の文字で終わるなど検索する文字列に条件を付けることができるからです。条件判断はプログラムの得意とするところですが、正規表現はわざわざプログラムを組まなくても手軽に使えるので便利です。

 まず、特定の文字から始まる文字列にマッチさせてみましょう。
 先ほどのテキストファイルにある4桁の数字がある行だけを抽出してみましょう。以下のようにコマンドを入力します。[0-9]は9から9までの数字にマッチ(一致)する事を意味します。[ ]内に文字を指定すると、その文字もしくは範囲などにマッチさせる事ができます。

grep '[0-9][0-9][0-9][0-9]' sample2.txt

拡張正規表現を使っていないので、冗長な書き方になっています。これが100桁の数字にマッチするとなると書くのが大変です。

そこで便利なのが拡張正規表現です。今のプログラム言語などでは当たり前に使える{ }の記述方法ですが、grepの場合はEオプションを指定する必要があります。{ }の中に数字を書くと直前のメタ文字の繰り返しになります。メタ文字というのは正規表現で使用される特別な文字の事を示します。ピリオドやバックスラッシュ、ブラケット(括弧)、$記号などが該当します。

grep -E '[0-9]{4}' sample2.txt

.
編集部おすすめ