今回はMacならではの検索について説明します。Macではfindコマンド以外に高速に検索できるコマンドがあります。また、全文検索もできるので便利でしょう。
今回もいつものようにサンプルで利用するファイル・ディレクトリはデスクトップのsampleディレクトリとしています。デスクトップにsampleディレクトリがない場合は作成しておいてください。(コマンド入力ならmkdir ~/Desktop/sampleとして作成することができます)

mdfindコマンド

 MacではOSのバージョン10.4からファイルの検索および内容の全文検索を行う機能が搭載されています。検索を行うと裏で動いているSQLiteで処理した結果が出力されるという仕組みです。

・MacのSpotlightで検索する
https://support.apple.com/ja-jp/guide/mac-help/mchlp1008/mac

・SQLite
https://ja.wikipedia.org/wiki/SQLite

 まずはmdfindコマンドで使えるオプションを見てみましょう。mdfindとだけ入力するとオプションとコマンドの例が表示されます。

 ちなみに裏で動いているSQLiteも以下のように-versionを付けて入力するとバージョン等が表示されます。バージョン指定を忘れてしまった場合には、コマンドプロンプトの状態で最後に.quitと入力すると元のコマンドラインの状態に戻れます。

 最初にmdfindを使って拡張子がtxtのファイルを検索してみましょう。以下のように入力します。

mdfind .txt

実行するとたくさん検索結果が表示されます。これは全体から全てのtxtの拡張子のファイルが検索されたためです(実際には本文に.txtが含まれる文字列も検索結果に出力されています)。mdfindは検索するディレクトリを指定しないと全体から検索されてしまいます。特定のディレクトリ内だけを検索するにはオプションを指定する必要があります。デスクトップ上にあるsampleディレクトリ内だけにある拡張子が.txtのファイルを検索するには-onlyinを指定します。-onlyinの後に検索対象とするディレクトリを指定します。

mdfind -onlyin ~/Desktop/sample .txt

カレントディレクトリ内を検索対象にすることもできます。まずは検索するディレクトリに移動します。

cd ~/Desktop/sample

次に-onlyinの後に./を指定し、その後に検索する単語を指定します。

mdfind -onlyin ./ .txt

ここまでの検索オプション指定だとファイル名だけでなく本文の内容も検索対象となっています。今回の例ではsampleディレクトリ内にあるtest.textの内容にsample.txtという文字があるため、検索結果にリストアップされています。

内容検索はしたくない、ファイル名だけを検索対象にしたい場合は-nameを指定します。以下のようにするとファイル名に.txtが含まれるファイルだけがリストアップされます。test.textファイルが検索結果に表れていないのがわかります。

mdfind -onlyin ./ -name .txt

検索した結果、一致する数だけを求める場合は-countを指定します。以下のようにすると-countの後に指定した文字列と一致した数が表示されます。

mdfind -onlyin ./ -count .txt

メタデータの取得

 mdfindコマンドの面白いところはファイル名や内容検索だけでなくメタデータでの検索も可能な点です。メタデータで検索といっても、そのファイルにどのようなメタデータが付加されているかを把握しなければ検索のしようがありません。
 Finderでもカラム表示にしてからファイルを選択すると右側にメタデータが表示されます。

しかし、Finderでメタデータを取得するのは手間と時間がかかります。特にファイルが複数ある場合はクリックして選択して確認していくだけで大変です。やはり、ここはコマンドで取得したいところです。そして、都合のいいことにメタデータを取得するコマンドmdlsが用意されています。何も指定しない場合、以下のように使い方が表示されます。

 mdlsコマンドの後にファイル名を指定すると、そのファイルのメタデータが表示されます。なお、メタデータを表示しているファイルは、これまでと同様デスクトップのsampleディレクトリ内にあります。

mdls test.text
mdls photo/img001.jpg
mdls screen/cap1.png

メタデータをXMLで取得することもできます。以下のようにするとtest.textのファイルのメタ情報がxml.txtファイルに保存されます。

mdls -plist xml.txt test.text

特定のメタデータだけを検索することもできます。ここではtest.textのメタデータからkMDItemFSSizeだけをリストアップしてみます。名前を変えれば様々な情報を検索できます。まず、test.textのメタデータを以下に示しておきます。ちなみに以下のデータはmdls test.text | pbcopyとしてペーストボードに転送(コピー)した後に貼り付けています。

_kMDItemDisplayNameWithExtensions      = "test.text"
kMDItemContentCreationDate             = 2022-03-02 15:00:00 +0000
kMDItemContentCreationDate_Ranking     = 2022-03-02 00:00:00 +0000
kMDItemContentModificationDate         = 2022-04-24 03:18:36 +0000
kMDItemContentModificationDate_Ranking = 2022-04-24 00:00:00 +0000
kMDItemContentType                     = "public.plain-text"
kMDItemContentTypeTree                 = (
    "public.plain-text",
    "public.text",
    "public.data",
    "public.item",
    "public.content"
)
kMDItemDateAdded                       = 2022-04-24 01:54:22 +0000
kMDItemDateAdded_Ranking               = 2022-04-24 00:00:00 +0000
kMDItemDisplayName                     = "test.text"
kMDItemDocumentIdentifier              = 470
kMDItemFSContentChangeDate             = 2022-04-24 03:18:36 +0000
kMDItemFSCreationDate                  = 2022-03-02 15:00:00 +0000
kMDItemFSCreatorCode                   = ""
kMDItemFSFinderFlags                   = 0
kMDItemFSHasCustomIcon                 = (null)
kMDItemFSInvisible                     = 0
kMDItemFSIsExtensionHidden             = 0
kMDItemFSIsStationery                  = (null)
kMDItemFSLabel                         = 0
kMDItemFSName                          = "test.text"
kMDItemFSNodeCount                     = (null)
kMDItemFSOwnerGroupID                  = 20
kMDItemFSOwnerUserID                   = 502
kMDItemFSSize                          = 10
kMDItemFSTypeCode                      = ""
kMDItemInterestingDate_Ranking         = 2022-04-24 00:00:00 +0000
kMDItemKind                            = "標準テキスト書類"
kMDItemLastUsedDate                    = 2022-04-24 03:18:33 +0000
kMDItemLastUsedDate_Ranking            = 2022-04-24 00:00:00 +0000
kMDItemLogicalSize                     = 10
kMDItemPhysicalSize                    = 4096
kMDItemUseCount                        = 30
kMDItemUsedDates                       = (
    "2022-04-23 15:00:00 +0000"
)
kMDItemUserModifiedDate                = (
    "2022-04-24 03:18:36 +0000"
)
kMDItemUserModifiedUserHandle          = (
    502
)

特定のメタデータを検索するには-nameを指定し、その後にメタデータ文字列を指定します。最後に検索対象となるファイル名もしくはパス名を指定します。

mdls -name kMDItemFSSize test.text

ファイル名部分にはワイルドカードも使えるのでmdls -name kMDItemFSSize *とするとカレントディレクトリ内にあるファイルのメタデータが表示されます。なお、ディレクトリの場合、kMDItemFSSizeはnullになります。

mdls -name kMDItemFSSize *

メタデータを利用して検索する

 メタデータ名で検索するにはmdlsでもよいかもしれませんが、条件を指定して検索するとなるとmdfindの出番です。mdfindではヘルプ表示でのコマンド例にもあるように条件を指定して検索することができます。
 例えば以前の連載でも取り上げたファイルサイズが0のファイルをカレントディレクトリ内から検索するには以下のように入力します。ここで注意点としては"でなく'で囲まないと正しい結果を得られません。

mdfind 'kMDItemFSSize==0' -onlyin ./

ファイルサイズが4以下の場合は以下のように不等号を指定します。

mdfind "kMDItemFSSize<=4" -onlyin ./

 メタデータを眺めると様々なものがありますが、画面キャプチャーした画像だけを検索してみましょう。画面キャプチャーしたPNG画像にはメタデータkMDItemCommentの内容がScreenshotという文字列になっています。Photoshopなどで作成したPNG画像には、このようなメタデータはありません。
 以下のようにするとカレントディレクトリ以下にある画面キャプチャーした画像だけがリストアップされます。

mdfind 'kMDItemComment==Screenshot' -onlyin ./

 他にも画面のどの範囲を切り取ったのかなど様々なメタデータがあります。次に画像のピクセル幅をキーにして検索してみます。画像のピクセル幅を示すメタデータのキーは以下のものがあります。

kMDItemPixelWidth   画像の横幅
kMDItemPixelHeight  画像の縦幅

以下のようにすると画像の横幅が64ピクセル以下の画像が検索されます。

mdfind 'kMDItemPixelWidth<=64' -onlyin ./

64ピクセルより大きい横幅の画像の場合は以下のようになります。

mdfind 'kMDItemPixelWidth>=64' -onlyin ./

画像は横幅だけでなく縦幅も同時に指定して検索したい場合もあります。メタデータの検索では複数の条件を指定できます。同時に複数の条件を満たす場合には&&を、どちらかの条件を満たす場合には||を指定します。

&& 左辺と右辺の条件の両方を満たす
||  左辺つ右辺の条件のどちらか片方を満たす
=   等しい
==  等しい
<   左辺が右辺より小さい
>   左辺が右辺より大きい
<=  左辺が右辺より小さいか等しい
>=  右辺が左辺より大きいか等しい
InRange(メタデータ名,最小値,最大値) 指定した範囲内の場合

以下の例では画像の横幅が64ピクセル以下で、なおかつ縦幅も32ピクセル以下の場合のファイルをリストアップします。

mdfind 'kMDItemPixelWidth<=64 && kMDItemPixelHeight<=32' -onlyin ./

以下の例では画像の横幅が64ピクセル以下または縦幅も8ピクセル以下の場合のファイルをリストアップします。

mdfind 'kMDItemPixelWidth<=64 || kMDItemPixelHeight<=8' -onlyin ./

日付を利用して検索

 今度は日付のメタデータを利用して検索してみます。なお、検索するファイルの日付と今日の日付は以下のようになっています。

まずは、今日更新されたかどうかを検索してみます。この場合、条件の所にプログラムのメソッドを指定できます。今日を示すメソッドは$time.today()です。  以下のようにコマンドを入力すると、カレントディレクトリ内で今日更新されたファイル等が検索されます。

mdfind 'kMDItemFSContentChangeDate>=$time.today()' -onlyin ./

次に前日(1日以内)から今日までに更新されたファイルを検索してみます。この場合もtoday()メソッドを利用できます。パラメーターに-1を指定すると前日になります。負数を指定すれば、前日、一昨日などの検索も可能です。

mdfind 'kMDItemFSContentChangeDate>=$time.today(-1)' -onlyin ./

 日付の指定方法には別の書き方もあります。以下のようにdate:の後に日付を示すtodayの文字列を指定すると今日変更されたファイル等が検索されます。

mdfind "date:today" -onlyin ./

昨日更新されたファイル等を検索する場合はyesterdayの文字列を指定します。

mdfind "date:yesterday" -onlyin ./

どちらの指定方法でも動作は同じなので状況に応じて使い分ければよいでしょう。

今日  date:today  $time.today()
昨日  date:yesterday  $time.yesterday()
今週  date:this week  $time.this_week()
今月  date:this month $time.this_month()
今年  date:this year  $time.this_year()

その他の検索

 メタデータ検索を利用すればディレクトリ(フォルダー)だけを検索することもできます。ディレクトリの場合はkind:の後にfolderを指定します。

mdfind "kind:folder" -onlyin ./

 kindの後に指定できる種類には以下のものがあります。

アプリケーション(以下の3種類を指定可能)
    kind:application
    kind:applications
    kind:app
音楽・ミュージック(以下の2種類を指定可能)
    kind:audio
    kind:music
ブックマーク(以下の2種類を指定可能)
    kind:bookmark
    kind:bookmarks
電子メール(以下の4種類を指定可能)
    kind:email
    kind:emails
    kind:mail message
    kind:mail messages
フォルダー(以下の2種類を指定可能)
    kind:folder
    kind:folders
書体・フォント(以下の2種類を指定可能)
    kind:font
    kind:fonts
画像・イメージ(以下の2種類を指定可能)
    kind:image
    kind:images
映像・ムービー(以下の2種類を指定可能)
    kind:movie
    kind:movies
PDF(以下の2種類を指定可能)
    kind:pdf
    kind:pdfs
プレファレンス(以下の2種類を指定可能)
    kind:system preferences
    kind:preferences
プレゼンテーション(以下の2種類を指定可能)
    kind:presentations
kind:presentation

 最後に画像を撮影した機器名をリストアップしてみましょう。撮影機器名はkMDItemAcquisitionModelという名前で記録されています。例えば以下のようになっています。

kMDItemAcquisitionModel = "iPhone 6s Plus"
kMDItemAcquisitionModel = "iPhone SE (1st generation)"
kMDItemAcquisitionModel = "iPhone SE (2nd generation)"
kMDItemAcquisitionModel = "iPhone XS Max"

撮影した写真がどの機種で撮影したものか調べるにはmdfindで以下のようにします。

mdfind 'kMDItemAcquisitionModel=*' -onlyin ./

この段階ではメタデータkMDItemAcquisitionModelが存在するファイルパスだけがリストアップされただけです。今度はmdlsコマンドを使って検索し機種名だけを表示するようにします。ここで単純に|(パイプ)で繋げてしまうとうまくいきません。そんな時に便利なのがxargsです。標準入力をコマンドのパラメーターとして渡してくれます。以下のようにすると機種名だけが表示されます。

 mdfind 'kMDItemAcquisitionModel=*' -onlyin ./ | xargs mdls -name kMDItemAcquisitionModel

これだと同じ行が検索された画像ファイルの数だけ表示されてしまいます。これを重複しないようにまとめる場合は以下のようにします。uniqは同じ内容の行があれば1つにまとめてくれます。

mdfind 'kMDItemAcquisitionModel=*' -onlyin ./ | xargs mdls -name kMDItemAcquisitionModel | uniq

メタデータには他にも撮影場所の緯度経度なども記録されています。メタデータを上手に利用すれば、より便利な検索ができるでしょう。他にもディレクトリをライブで監視する機能もあります。興味が出てきたら様々なオプションとアイデアを組み合わせてみてください。

著者 仲村次郎
いろいろな事に手を出してみたものの結局身につかず、とりあえず目的の事ができればいいんじゃないかみたいな感じで生きております。