Spotlightのインデックスを検索するコマンド mdfind は速くて便利で優れもの。使わない手はありません。けどやや曲者です。書き方に癖あるし上手く動かないことや異常を発生させることもあります。
mdls
mdfind を使う前に mdls について知っておく必要があります。mdls はインデックスされた情報を取得し表示します。
ターミナルで mdls とタイプしてファイルをドラッグするとつらつら〜とインデックスされたメタ情報が出てきます。これらを mdfind で検索することができます。
kMDItemBitsPerSample = 32 kMDItemColorSpace = "RGB" kMDItemContentCreationDate = 2015-01-06 17:53:16 +0000 kMDItemContentCreationDate_Ranking = 2015-01-06 00:00:00 +0000 kMDItemContentModificationDate = 2015-01-07 02:53:12 +0000 kMDItemContentType = "public.png" kMDItemContentTypeTree = ( "public.png", "public.item", "public.png", "public.data", "public.image", "public.content" ) kMDItemCreator = "Adobe Illustrator CS6 (Macintosh)" kMDItemDateAdded = 2023-11-01 05:13:55 +0000 kMDItemDateAdded_Ranking = 2023-11-01 00:00:00 +0000 kMDItemDisplayName = "GEORGIA!.png" kMDItemFSContentChangeDate = 2023-11-01 05:13:55 +0000 kMDItemFSCreationDate = 2023-11-01 05:13:55 +0000 kMDItemFSCreatorCode = "" kMDItemFSFinderFlags = 0 kMDItemFSHasCustomIcon = (null) kMDItemFSInvisible = 0 kMDItemFSIsExtensionHidden = 0 kMDItemFSIsStationery = (null) kMDItemFSLabel = 0 kMDItemFSName = "GEORGIA!.png" kMDItemFSNodeCount = (null) kMDItemFSOwnerGroupID = 20 kMDItemFSOwnerUserID = 501 kMDItemFSSize = 3071678 kMDItemFSTypeCode = "" kMDItemHasAlphaChannel = 0 kMDItemInterestingDate_Ranking = 2015-01-06 00:00:00 +0000 kMDItemKind = "PNGイメージ" kMDItemLogicalSize = 3071678 kMDItemOrientation = 0 kMDItemPhysicalSize = 3072000 kMDItemPixelCount = 2029144 kMDItemPixelHeight = 1417 kMDItemPixelWidth = 1432 kMDItemProfileName = "sRGB IEC61966-2.1" kMDItemResolutionHeightDPI = 300 kMDItemResolutionWidthDPI = 300 kMDItemTitle = "P1-P4"
取得できるメタデータの中で特筆すべきは、パッケージであることを判別するkeyがあることです。メタデータはいろんなツールで取得できますが、パッケージを判断できるのは mdls だけ。
kMDItemContentTypeTree が、それです。
kMDItemContentTypeTree = ( "com.apple.mainstage.concert", "public.item", "com.apple.package", "com.apple.mainstage.concert", "public.directory" )
パッケージになったファイルを探し出したいという希望から、このコマンドを知ることになったんです。
さて、こうしたメタデータを元に検索します。Finder での Spotlight 検索はほとんど役に立ちませんが、コマンドラインでは威力を発揮します。find や locate より圧倒的に速く、しかもメタデータで検索できますし、パッケージだって見つけちゃいます。
mdfind
コマンド mdfind はSpotlight検索するコマンドです。ですからインデックスされていることが前提です。Spotlight?役立たずだからインデックスなんか作ってないぜ!なんてイキってないで、作っておきましょうよ。
mdfind の強みは、mdls で確認出来るメタデータで検索できることですが、書き方がちょっと難しかったので順を追いましょう。
普通の検索
-name …名前で検索
mdfind -name 'img_0123.jpg'
ファイル名「img_0123.jpg」を検索します。「type2img_0123.jpg」もヒットします。
-onlyin … 特定ディレクトリ内を検索
mdfind -onlyin ~/Desktop -name '0123'
デスクトップ内でファイル名に ‘0123’ が含まれるファイルを検索します。
-count ヒット数を表示する
mdfind -count -name 'さぼてん坊や'
「さぼてん坊や」が含まれるファイルが何個あるか、その数を結果に表示します。
これを例えば、mdfind -count 'さぼてん坊や'
と、-name を付けずに検索すると、内容に踏み込んで検索します。Spotlightらしいですね。
mdls のメタデータで検索
メタデータで検索するには mdls の各keyを指定します。
mdfind 'key == value'
mdfind 'key == "value"'
基本がこの形です。メタデータ検索指定の全体をシングルクォーテーションで囲みます。value は必要に応じてダブルクォーテーションで括ります。mdls の結果と同じってことですね。ざっくり、テキストなら括る、数字なら括らないとか、そんな感じかなと。
== は「等しい」ですので、<= とか >= とか、演算子を使うこともできます。ファイルサイズが〜以上とか、幅が〜以下とかそういう。
ファイルタイプを検索
種類というかファイルタイプというか拡張子というか、それで検索することは簡単そうに思えてとても厄介でした。
まず、mdls で調べたところ、kMDItemFSTypeCode の値にファイルタイプが書かれていました。しかし多くのファイルが kMDItemFSTypeCode = “” と、値がありません。あったりなかったりして不便なのでこれは検索では使えません。
kMDItemKind というキーがありまして、これがいわゆるFinder上で「種類」として扱われるメタデータです。 “PNGイメージ”、”QuickTimeムービー” みたいな感じで書かれているアレです。
種類で検索しようとするとこういう書き方になります。
mdfind 'kMDItemKind=="QuickTimeムービー"'
mdfind 'kMDItemKind=="PNGイメージ"'
ちょっと何だかなーですね。値の部分にワイルドカードが使えるというので、早速試してみましたら、これがまたうんともすんとも言わない。
動かない例 mdfind 'kMDItemKind=="png*"'
もしかして大文字小文字区別するのかと思ったけど、こう書いてもまったく動かない。
動かない例 mdfind 'kMDItemKind=="PNG*"'
いろんな書き方をテストしてみた結果はこうでした。
動いた例 mdfind 'kMDItemKind=="*png*"'
ワイルド★を前後に付ければ検索動きました。大文字小文字関係なかった。なぜ前後に?
しかしこういう「種類」ではMIMETypeやTypeや拡張子検索の代わりにはなりませんね。そんなことが出来ないなんて・・・と、間抜けなことを考えていたら、やっぱここの筆者はアホの固まりですね。kMDItemContentType あるいは kMDItemContentTypeTree がちゃんとありました。
kMDItemContentType は、PNGだとこう書かれています。
kMDItemContentType = "public.png"
kMDItemContentTypeTree を確認すると次のように書かれていました。
kMDItemContentTypeTree = ( "public.png", "public.item", "public.png", "public.data", "public.image", "public.content" )
これやがな。これでイケる。
ということで、以下で正しくファイルタイプの検索できました。
mdfind -onlyin ~/Desktop 'kMDItemContentType == "*png"'
mdfind -onlyin ~/Desktop 'kMDItemContentType == *png'
これでイケました。基本の理解がまったくないので、なぜ "*png"
でも *png
でも良いのか、なぜ kMDItemKind では *png*
と、無理矢理前後に * を付けないといけないのか、そういう理由がさっぱりわかりません。そういうところも含めて、とても難しいコマンドです。
パッケージを検索
パッケージを検索するにはこうです。
mdfind 'kMDItemContentTypeTree == "*package*"'
特定フォルダ内を探すなら、例えばデスクトップ内だとこうですか。
mdfind -onlyin ~/Desktop 'kMDItemContentTypeTree == "*package*"'
パッケージを見つけられるのは mdfinde だけ!
複数のメタデータ
さて本番はここからです。検索したい key が複数ならどう書くでしょう。
例として、ファイルサイズが 3071678 でカラースペースが RGB の GEORGIA!.png をデスクトップ上で探してみましょう。
ファイルサイズ のkeyはいくつもありますが、ここでは kMDItemFSSize です。なぜ kMDItemFSSize であるのか理由がありますがそれは後ほど。
カラースペースは kMDItemColorSpace ですね。
ファイル名は GEORGIA!.png です。-name でもいいし、より正確に判断したいなら kMDItemFSName を使うのもありでしょう。
以上、いずれの条件も満たすファイルを探します。このように書きます。
mdfind -onlyin ~/Desktop 'kMDItemFSSize==3071678&&kMDItemColorSpace=="RGB"&&kMDItemFSName=="GEORGIA!.png"'
※ 全体をシングルクォーテーションで括っています。
※ 各条件を &&(かつ)で繋いでいます。つまりここは || (または) でもイケるってことです。
※ mdls で確認出来るとおり、テキストはダブルクォーテーションで括ってます。
例では詰めて書きましたが、シングルクォーテーションの中でスペース入れてもOKみたいです。見やすいようにスペース入れたのが↓
mdfind -onlyin ~/Desktop 'kMDItemFSSize == 3071678 && kMDItemColorSpace == "RGB" && kMDItemFSName == "GEORGIA!.png"'
最初、全体をシングルクォートするのが判っていなくて、 ‘条件1’ && ‘条件2’ みたいに書いてて「なぜ動かないんだ」と悶えていましたが、全体で一個という真っ当な考えでした。
余談: ところで、ファイルサイズに kMDItemFSSize を使うこと、さらにファイルサイズ 3071678 とかどう考えても一般の人間が普段行う検索の見本として逸脱しているように見えるこの例についてお話します。これはね、他の全然関係ない javascript をテストしていて、特定一意のファイルのね、サイズとファイル名だけを取得できたので、それを元にローカルのパスを知りたいという、そういう目的から来ています。取得できたサイズがどのサイズなのか、答え合わせをしてみたら kMDItemFSSize でした。
最初、サイズとファイル名だけを頼りに find などを使ってみたところ、遅すぎて検索結果を見ることもできませんでした。mdfind を使ってみてビックリ。全ローカル環境から一瞬で見つけ出します。これはスゴいす。
というわけで、慌てて全ボリュームのSpotlightインデックスを作り始めましたよ!
↑
インデックスなんか作らないぜとイキってたのはお前だったのか
mdfind の落とし穴
さて、超高速で融通効きまくり、抜きん出た威力の mdfind には壮絶な落とし穴もあります。
このコマンドを利用してせこせこと作り物をしていたんですが、テストスクリプトを走らせると他のいろんなツールを巻き込んでハングしてしまうことが多発、強制終了も再起動も出来ず電源長押しを何度も繰り返している内にカーネルパニックは出るわいくつかのファイルがぶっ飛んで壊れるわ散々な目に遭いました。
この悪辣なエラーの正体は Spotlightインデックスの不調でした。インデックスのどこかで作成に失敗したか壊れたかいずれにせよ何らかのエラーがあったようです。すべてのボリュームのインデックスを一度消して作り直してみたところ、悪辣エラーは消えました。テストスクリプトも当たり前のように正常動作します。
インデックスはたまに壊れるようで、普段気付きませんが mdfind をガシガシ使っていると被害が表面化しやすくなると思います。想定外の妙なエラーが起きたら、一般的なトラブルシュートの前に何をさておいてもSpotlightのインデックスを全部消して作り直す、これをやれば乗り切れると思います。
do shell script で使うとき
do shell script でターミナルのコマンドを書くときはコマンド全体をダブルクォートで囲みます。
do sell script "date | pbcopy"
コマンドの中でダブルクォートがある場合はシングルに変えてやります。
do shell script "mdfind -name '狂った花嫁' | LANG=ja_JP.UTF-8 pbcopy"
コマンドの中にシングルクォートとダブルクォートがあるときはダブルクォートにエスケープ対処が必要ですね。
do shell script "mdfind 'kMDItemFSSize == 3071678 && kMDItemFSName == \"GEORGIA!.png\"'| LANG=ja_JP.UTF-8 pbcopy"
これを計算式で記述しようとすれば、エスケープをさらにエスケープしないといけないのでこうなります。
"do shell script \"mdfind 'kMDItemFSSize == 3071678 && kMDItemFSName == \\\"GEORGIA!.png\\\"' | LANG=ja_JP.UTF-8 pbcopy &\""
これで合ってましたっけ?💦💦