mac ファイル検索コマンドの使い分け|find・mdfind・rgの選び方
画面の検索で出てこなかったファイルを、ターミナルから探し直す。mac ファイル検索コマンドという言葉で調べる人の多くは、この順番でここへたどり着きます。覚えるべきコマンドは実のところ数個しかなく、問題になるのは名前ではなく選び方です。索引を引くのか、ディスクを歩くのか。名前で当てるのか、中身で当てるのか。この2本の軸で整理すると、どの場面でどれを打てばよいかが決まり、結果が空で返ってきたときの原因もその場で説明がつきます。
索引を引くコマンドと、ディスクを歩くコマンド
Macのファイル検索コマンドは、大きく2つの系統に分かれます。片方はSpotlightが作ったメタデータの索引に問い合わせるもので、mdfind と mdls がこれにあたります。もう片方はファイルシステムを実際にたどるもので、find や grep -r、fd、rg が入ります。
違いは速度と死角に出ます。索引を引く側は、何十万件のファイルがあっても応答が一瞬で返ります。すでに調べ上げた表を引いているだけだからです。そのかわり、索引に載っていないものは何を打っても出てきません。ディスクを歩く側は死角がなく、そこに在るものは在ると答えます。そのかわり、対象のツリーが大きいほど時間がかかり、ホームフォルダ全体を対象にすると数十秒かかることもあります。
この差は、探しものの性質で使い分けられます。書類やソースコードのように普段から扱っているファイルを、名前のかけらだけ覚えている状態で探すなら、索引を引くほうが速い。逆に「あるはずなのに出てこない」という状態、つまりすでに一度検索に失敗している状態なら、索引を疑うべきなので歩く側を使います。ここを取り違えて、索引に載っていないファイルを mdfind で何度も打ち直すのが、いちばん時間を落とす形です。
検索が空で返ったときの読み方も、この2系統で変わります。歩く側が空を返したなら、その範囲にそのファイルは無いと言い切れます。索引を引く側が空を返したときは、無いのか、索引に載っていないのかが区別できません。同じ「0件」でも情報量が違うという点は、順番を決めるうえで押さえておく価値があります。
もう1つの軸が、名前で探すか中身で探すかです。名前で探すほうが結果は少なく読みやすく、中身で探すほうは当たりますが件数が膨らみます。実際の作業では、名前で範囲を絞ってから中身を読む、という順番が扱いやすくなります。
findを読める件数に落とす書き方
find は素朴に打つと、権限のないディレクトリのエラーと大量の結果で画面が埋まります。読める形にするための指定は4つです。
- 種類を絞る。
-type fでファイルだけ、-type dでディレクトリだけ - 深さを切る。
-maxdepth 3を付けると、それより下へは降りない - 大文字小文字を無視する。
-nameの代わりに-inameを使う - エラーを捨てる。末尾に
2>/dev/nullを付けると権限のないディレクトリの警告が消える
find ~/Documents -maxdepth 3 -type f -iname "*見積*" 2>/dev/null
日付とサイズの条件も、覚えておくと当たりが早くなります。-mtime -7 は7日以内に更新されたもの、-size +10M は10メガバイトを超えるものです。「先週さわった大きめのファイル」という記憶は、この2つを並べればほぼそのまま条件に翻訳できます。
除外にも書き方があります。-not -path "*/node_modules/*" のように、通り道の名前で丸ごと外せます。開発用のディレクトリを抱えたホームフォルダでは、この1行を足すかどうかで結果の件数が桁で変わります。
複数の条件をつなぐときは、並べれば「かつ」になり、-o を挟むと「または」になります。「または」を使うときは丸括弧で囲む必要があり、シェルに解釈されないよう \( -name "*.png" -o -name "*.jpg" \) のようにバックスラッシュで打ち消します。ここは書き方が独特で、うまく動かない相談の多くがこの括弧の扱いです。
パターンを引用符で囲むのは、シェルに先に展開させないためです。find . -name *.log と裸で書くと、カレントディレクトリに .log ファイルがあった場合にシェルがその名前へ置き換えてしまい、意図と違う検索になります。-name "*.log" と囲めば、パターンのまま find へ渡ります。
mdfindが速いかわりに抱えている死角
mdfind はSpotlightと同じ索引をシェルから引きます。mdfind -onlyin ~/Documents "請求" のように範囲を絞れ、mdfind -name report なら名前だけに限定できます。返りは一瞬で、対応形式なら本文のテキストまで対象に入るため、当たったときの効率は他のどのコマンドより高くなります。
問題は、空で返ってきたときにその理由が画面に出ないことです。索引から外れる条件は主に4つあります。システム設定のSpotlightにある検索のプライバシーに登録されたフォルダ、名前が .noindex で終わるフォルダ、zipファイルやディスクイメージの中身、そして索引作成が止まっているボリュームです。どれも「ファイルが無い」のではなく「索引に無い」だけなので、find に切り替えれば普通に出てきます。
外付けディスクが対象になっているかは mdutil -s /Volumes/ディスク名 で確認できます。Indexing disabled と返ってきたら、そこを mdfind で探しても答えは出ません。個別のファイルが索引に何を持っているかは mdls ファイル名 で一覧でき、期待した属性が空なら取り込みが済んでいないと判断できます。
locate を思い出す人もいますが、こちらは週次で更新されるデータベースを引く仕組みで、macOSでは初回に手動で有効化する必要があり、作った直後のファイルは載っていません。日々動くファイルを探す用途では、mdfind か find のどちらかに寄せたほうが混乱が少なくなります。
5つの手段を1つの表に並べる
打つ前に選ぶための材料として、性質を並べておきます。
| 手段 | 索引を使う | 隠しファイル | 無視設定の対象 | 大きなツリーでの速さ |
|---|---|---|---|---|
| mdfind | 使う | Spotlightと同じ死角 | 対象外 | 一瞬 |
| find | 使わない | 見る | 対象外 | 遅い |
| grep -r | 使わない | 見る | 見る | 遅い |
| rg・fd | 使わない | 既定では飛ばす | 既定では飛ばす | 速い |
| locate | 週次のDB | 見る | 対象外 | 速い |
この表は行ではなく列で読みます。どれが優れているかを決める表ではなく、いま走らせる検索で「どの死角なら許容できるか」を選ぶための表です。急いでいて、普段から扱っているファイルなら上の行。一度失敗していて、確実に答えが欲しいなら真ん中の行。ソースコードのツリーの中を何度も探すなら下の行、という読み方になります。
更新の新しい順に見たい場面も多く出てきます。ls -lt はカレントディレクトリを更新順に並べ、find . -newer 基準ファイル は指定したファイルより後に更新されたものを返します。「さっき保存したはずのファイル」を探すときは、名前を思い出すより時間で絞るほうが早く着きます。
中身から探すときに効いてくる既定値の違い
中身の検索は grep -rn が基本形です。-r でディレクトリを再帰的にたどり、-n で行番号を出します。macOSに最初から入っているBSD版のgrepは、隠しファイルもバイナリも含めて素直に全部を見に行きます。
近年よく使われる ripgrep(コマンド名は rg)と fd は、既定値が違います。この2つは初期状態で、ドットで始まる隠しファイルと、.gitignore に書かれたファイルを飛ばします。ソースコードのツリーの中では扱いやすい既定値ですが、それ以外の場所では取りこぼしの原因になります。
grep -rn "APIキー" ~/work
rg -uu --hidden "APIキー" ~/work
fd -HI "\.env$" ~/work
grepでは見つかるのに rg では出てこない、という状態は不具合ではなく、この既定値の差です。rg なら -uu と --hidden、fd なら -HI を足すと無視設定が外れます。設定ファイル、認証情報、エディタの作業ファイルを探すときは、この指定の有無で結果が変わります。
件数が多すぎるときは、まず名前で絞ってから中身を読む形に組み替えます。find で候補のファイルを出し、その一覧に対してだけ grep を掛ければ、対象は数千件から数十件に落ちます。中身の検索を最初から広い範囲に掛けないのが、待ち時間を短くするいちばん単純な方法です。
見つけたパスを次の作業へ渡す
検索は目的ではありません。目的は、見つけたファイルを開く、名前を変える、移動する、別のコマンドに渡すのどれかです。ここでつまずくのは、たいていファイル名に含まれる空白です。
find の結果をそのまま次のコマンドへ渡すなら、-exec を使うか、-print0 と xargs -0 を組み合わせます。空白や日本語を含む名前でも、区切りが壊れません。
find . -name "*.png" -print0 | xargs -0 -n1 open -R
open -R パス は、そのファイルを選択した状態で画面のフォルダを開きます。シェルで見つけたものを目で確かめたいときに、窓を探し回らずに済みます。逆向きの移動も用意されていて、Appleはターミナルのユーザガイドにこう書いています。
Finderウインドウのパスバーを使用して、作業ディレクトリをあるフォルダの場所に設定した状態で、新しい「ターミナル」ウインドウやタブを開くことができます。Finderウインドウの下部にパスバーが表示されていない場合は、「表示」>「パスバーを表示」と選択します。 出典: support.apple.com
この2つを覚えておくと、パスをコピーして貼り付ける動作をかなり減らせます。それでも残るのは、窓を切り替えるという動作そのものです。
結果をその場で読みたいときは、less に流すか、件数だけ数える形にします。find . -name "*.md" | wc -l と打てば、まず何件あるのかが分かります。件数が3桁なら条件が緩すぎるので、深さか日付を足して絞り直す。この判断を先に入れておくと、長い一覧を目で追う時間が減ります。
危ない操作を混ぜるときは、確認の一手を挟みます。削除や移動を find の結果に対して走らせる前に、同じ条件で -print だけを実行して対象を目で見る。この順番を守るだけで、条件を1文字打ち間違えたときの被害が変わります。特に -delete は確認なしで消えるため、-print で見てから置き換える形が扱いやすくなります。
検索の後に窓を何回切り替えているか
コマンドを覚えても短くならない工程があります。フォルダの窓で結果を見て、シェルで処理し、また目で確かめるために戻る。この往復です。1回あたりは数秒でも、1日に何十回も繰り返します。詰まっているのは難しさではなく回数です。
ここで効いてくるのが、フォルダとターミナルとAIが同じ窓にあるという配置です。フォルダ一覧とコマンド入力が同じ作業ディレクトリを共有していれば、パスのコピーと窓の切り替えが工程から消えます。検索そのものが速くなるのではなく、検索の前後にあった移動が無くなる、という効き方です。
この配置で何ができるのかはできることに整理してあり、2画面型やターミナル常駐型など、別の課題を解いている道具との違いは他のファイル管理との比較で比べられます。手元を離れているあいだに続きを見たい場合の扱いはiPhone・iPadから続きをにまとまっています。
道具を替える前に、1週間だけ回数を数えることをすすめます。検索の後に窓を切り替えた回数が少ないなら、この記事の前半にある find の絞り込みと rg の既定値を押さえるだけで足ります。数が多いなら、それは検索コマンドの問題ではなく配置の問題です。
よくある質問
findとmdfindはどちらを先に使えばいい?
名前のかけらを覚えていて、普段から扱っているファイルなら mdfind が速く返ります。すでに画面の検索で見つからなかったファイルなら、索引に載っていない可能性が高いので find を使います。mdfind が空で返ったからといってファイルが無いとは判断できない、という点だけ押さえておけば使い分けは迷いません。
findで大量のエラーが出るのを消すには?
権限のないディレクトリに入ろうとしたときの警告なので、コマンドの末尾に 2>/dev/null を付けると表示されなくなります。あわせて -maxdepth 3 で深さを切り、-type f でファイルだけに絞ると、結果そのものも読める件数に落ちます。エラーを消すより先に範囲を狭めるほうが効きます。
ripgrepで隠しファイルが検索されないのはなぜ?
ripgrepとfdは、初期状態で隠しファイルと .gitignore に書かれたファイルを飛ばす設計になっているためです。ripgrepなら --hidden と -uu、fdなら -HI を付けると無視設定が外れます。設定ファイルや認証情報を探すときは、この指定を付けないと取りこぼします。
ファイル名に空白があるとコマンドが失敗するのはなぜ?
空白がそのまま引数の区切りとして扱われ、1つのパスが2つに割れるためです。find の結果を渡すときは -print0 と xargs -0 を組み合わせるか、-exec コマンド {} \; の形を使います。手で打つ場合は、パス全体を引用符で囲むだけで防げます。