はじめに
最近実務でgrepコマンドを使う機会が何度かあったのですが、全然使いこなせていないと感じることが多かったので、これを機に体系的に学び直してみました。
対象読者
- grepコマンドについて知らない、使ったことがない方
- 業務でたまにgrepコマンドを使うが、その都度オプションなどを調べている方
- grepコマンドを体系的に学んだことがない方
grepコマンドとは
指定した文字列やパターンに一致する行をディレクトリやファイルから検索するコマンドです。
様々なオプションが用意されていたり正規表現が使えることから、柔軟な検索が可能です。
grepの基本操作
以下が基本文法になります。
$ grep [オプション] 文字列 <検索対象のファイル or ディレクトリ>
指定できるオプション一覧
オプション | 説明 |
---|---|
-r | ディレクトリとその中にあるディレクトリから検索 |
-v | 特定の文字列を除外し検索 |
-E | 拡張正規表現を使い検索 |
-l | 検索結果にファイル名のみ出力 |
-c | 検索で一致した行数を出力 |
-h | 検索結果にファイル名を出力しない |
-i | 大文字・小文字区別せずに検索 |
-x | 完全一致する行を検索 |
-n | 検索結果に行番号を出力 |
-o | 検索に一致した文字列のみを出力 |
-w | 指定した単語に完全一致する行を検索 |
-C | 検索結果に一致した行とその前後の行を指定した数だけ表示 |
-A | 検索結果に一致した行とその後の行を指定した数だけ出力 |
-B | 検索結果に一致した行とその前の行を指定した数だけ出力 |
-L | 検索に該当しなかったファイルを出力 |
--exclude="*.拡張子" | 特定の拡張子のファイルを検索から除外 |
--exclude-dir=path | 特定のディレクトリを検索から除外 |
--include="*.拡張子" | 特定の拡張子のファイルをのみを検索対象とする |
オプションは以下のように複数組み合わせても使えます。
$ grep -nE "user_(path|url)"
# -Eで拡張正規表現を使い検索
# -nで検索結果に行数を出力する
$ grep -rohE "[ぁ-んァ-ン亜-熙 -】]+" ./*
# -rディレクトリの中のディレクトリも検索対象とする
# -oと-hで検索に一致した文字列のみを表示(ファイル名は出力しない)
# -Eで拡張正規表現を使って検索
grepコマンドの使用例
これまで紹介したオプションなどの情報を元に、どのような使用例があるのかを例付きで記載していきます。
(※一部の例では筆者が慣れ親しんでいるRailsのコードを題材とさせていただいております🙏)
指定したファイルから特定の文字を検索
例)ログファイルからERROR
を含む行を検索
$ grep "ERROR" development.log
指定したディレクトリから特定の文字を検索
例)usersディレクトリ内のファイルからname
を含む行を検索
$ grep "name" users/*
-rでディレクトリとその中にあるディレクトリから検索
ディレクトリの中にあるディレクトリも検索対象に含めたい場合は、-r
オプションをつける必要があります。
例)controllersディレクトリとその中にあるディレクトリからUser.create
を含む行を検索
$ grep -r "User.create" app/controllers/*
AND検索
パイプ(|
)でgrepを繋げてAND検索が行えます。
例)controllersディレクトリからuser
とprofile
を含む行を検索
$ grep -r "user" app/controllers/* | grep profile
後述する-v
オプションと組み合わせることで「ooを含むがooは含まない」といった検索も可能です。
例)ログファイルからstatus
を含むかつ200
が含まれていない行を検索
$ grep "status" development.log | grep -v 200
OR検索
-eオプションを使うことOR検索が行えます
例)ログファイルからINSERT
またはUPDATE
を含む行を検索
$ grep -e "INSERT" -e "UPDATE" development.log
-vで特定の文字列を除外し検索
例)ログファイルからINFO
を含まない行を検索
$ grep -v "INFO" development.log
-Eで拡張正規表現を使い検索
拡張正規表現とは基本の正規表現を拡張したものです。
たとえば以下の記法は拡張正規表現に該当します。
-
+
:直前の文字の1回以上の繰り返し -
?
:直前の文字の0回もしくは1回の繰り返し -
a|b
:左右いずれかの文字列
例)左右いずれかの文字列に該当する行を検索
$ grep -E "user_(path|url)"
例)oo(1文字以上のアルファベット)_pathという文字列に該当する行を検索
$ grep -E "[a-zA-Z]+_path"
-lで検索結果にファイル名のみ出力
特定の文字列を含む行があるファイル名のリストを作りたい場合など有効です。
例)-rと組み合わせてcontrollersディレクトリとその中にあるディレクトリからApplicationControllerクラスを継承しているControllerクラスのファイル名を出力
$ grep -rl "< ApplicationController" app/controllers/*
users_controller.rb
posts_controller.rb
-cで検索で一致した行数を出力
例)modelsディレクトリからUser.create
を含む行の数をファイルごとに出力する
$ grep -c "User.create" app/models/*
app/models/user.rb:1
app/models/post.rb:2
-hで検索結果にファイル名を出力しない
該当する行だけを表示したい場合は有効です。
例)controllersディレクトリからcreate
を含む行を検索し、行のみを出力
$ grep -h "create" app/controllers/*
before_action :create_posts, only: [:create, :update]
def create
User.create(post_id: @post.id)
def create_posts
Post.create
def create_profile
user.create_profile
-iで大文字・小文字区別せず検索
例)ログファイルから大文字小文字区別せずerror
を含む行を検索
$ grep -i "error" development.log
-xで完全一致する行を検索
例えばuser
とadmin_user
の2つが存在し、user
のみを検索したい場合などに有効です。
例)modelsディレクトリからuser.update(name: name)
に完全一致する行を検索
$ grep -x "user.update(name: name)" app/models/*
-nで検索結果に行番号を出力する
例)modelsディレクトリからUser.create
を含む行を検索し行番号付きで出力
$ grep -n "User.create" app/models/*
app/controllers/users_controller.rb:2 before_action :create_posts, only: [:create, :update]
app/controllers/users_controller.rb:11 def create
app/controllers/users_controller.rb:12 User.create(post_id: @post.id)
app/controllers/users_controller.rb:37 def create_posts
app/controllers/users_controller.rb:38 Post.create
user.rb:20 def create_profile
user.rb:21 user.create_profile
-oで検索に一致した文字列のみを出力
正規表現を使って絞り込んだ結果、どのような文字列が該当するのかを確認したい場合に有効です。
1行の中に複数一致する場合、それぞれ一致する文字列を別行で表示します。
$ echo "apple banana apple" | grep -o "apple"
apple
apple
例)ログファイルからどのテーブルに対してSELECT文が発行されているかを出力
$ grep -o "SELECT * FROM [a-zA-Z]" development.log
development.log:SELECT * FROM users;
development.log:SELECT * FROM posts;
-wで指定した単語に完全一致する行を検索
単語として完全一致する行を検索します。
単語の前後が英数字やアンダースコアの場合はマッチしません。
# 以下はマッチする
$ echo "User" | grep -w "User"
$ echo "User.create" | grep -w "User"
# 以下はマッチしない
$ echo "Users" | grep -w "User"
$ echo "User1" | grep -w "User"
$ echo "User_" | grep -w "User"
例)user
という単語に一致する行だけを検索する(user_oo
やoo_user
などは除外したい)
grep -w "user" app/models/*
-Cで検索結果に一致した行とその前後の行を指定した数だけ出力
検索行の前後でどのような処理が呼ばれているかを確認したい場合に有効です。
例)user.rbファイルから、Profile.createを含む行を検索し、前後5行を出力
$ grep -C 5 "Profile.create" user.rb
-Aで検索結果に一致した行とその後の行を指定した数だけ出力
検索行の後にどのような処理が呼ばれているかを確認したい場合に有効です。
例)ログファイルでSELECT * FROM users;
を含む行を検索し、後の5行を出力
$ grep -A 5 "SELECT * FROM users;" development.log
-Bで検索結果に一致した行とその前の行を指定した数だけ出力
検索行の前でどのような処理が呼ばれているかを確認したい場合に有効です。
例)ログファイルでERROR
を含む行を検索し、前の10行を出力
$ grep -B 10 "ERROR" development.log
-Lで検索に該当しなかったファイルを出力
特定の処理を定義できていないファイルを探したい場合など有効です。
例)controllersディレクトリとその中にあるディレクトリからApplicationControllerクラスを継承していないControllerクラスのファイルを出力
$ grep -rL "< ApplicationController" app/controllers/*
特定の拡張子のファイルをのみを検索対象とする
--include="*.<対象としたい拡張子>"
とすれば指定した拡張子のファイルだけを検索します。
例)appディレクトリとその中にあるディレクトリかつ、拡張子が.rb
のファイルからUserを含む行を検索
$ grep -r "User" app/* --include="*.rb"
特定のディレクトリを検索から除外
--exclude-dir=<除外するディレクトリのパス>
とすれば指定したディレクトリ以外で検索します。
例)appディレクトリからviewsディレクトリを除外した上でUserを含む行を検索
$ grep -r "User" app/* --exclude-dir=views
特定の拡張子のファイルを検索から除外
--exclude="*.<除外する拡張子>"
とすれば指定したファイル以外で検索します。
例)appディレクトリから拡張子が.html
のファイルを除外した上でUserを含む行を検索
$ grep -r "User" app/* --exclude="*.html"
おまけ
正規表現を活用した例
これまで紹介したオプションだけでなく、正規表現を使うことでより柔軟な検索が可能です。
以下で正規表現を使用した検索例をいくつか紹介します。
例1)先頭にuser_
が付いているメソッドを検索
grep -r "def user_.*" app/*
例2)末尾に_user
がついているメソッドを検索
grep -rE "def.*_user$" app/*
例3)メールアドレスらしき文字列を検索
grep -E '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-z]{2,}'
例4)空白文字で始まる行を検索
grep "^[[:space:]]" template.txt
参考にさせていただいた記事・ドキュメント