find コマンドを使ってファイルを検索する方法
ターミナルでファイルを検索する時に重宝する find コマンドの使い方を紹介します.
ファイルに限らずディレクトリも検索することができたり, 何日前に変更されたファイルを検索できたりほかにも色々な条件で検索することができます.
プログラミングする時にはなにかと特定のファイルを検索する必要に迫られるので, そういう時に find コマンドの使い方を覚えておくと役立つこと間違いなしです.
しかも見つけだしたファイルに対して, なんらかの操作をしたいといった場合も多々出てくると思いますので, そういう時にも find コマンドは外部のコマンドを探し出したファイルに対して実行することができるので役立ちます.
今回は GNU の find コマンドを想定したいと思います.
え, find コマンドって種類があるの? と思われた方もいらっしゃるかもしれませんが, 実はほかにも BSD の find コマンドというものがあります. macOS を使われている方は標準で BSD の find コマンドがインストールされています.
なので GNU の find コマンドを macOS で使用するためにはインストールしなくてはなりません.
僕は macOS を使っているので macOS に GNU find をインストールする方法を紹介したいと思います.
macOS に GNU find をインストールする場合 Homebrew というパッケージマネージャを使うと手軽にインストールすることができるのでオススメです.
Homebrew をインストールしていなくて, どのようにインストールすればいいのかお知りになりたい方は Homebrew のインストールと基本的な使い方 を参考にされてインストールしていただければと思います.
GNU の find コマンドをインストールする
Homebre がインストールされて使える状態になったら早速次のコマンドで GNU find をインストール出来ます.
brew install findutils
あれ? find じゃなくて findutils なの? と思われた方, 実は GNU find は findutils というパッケージの一つに入ってます.
なので findutils をインストールすると find もインストールされるというわけです.
findutils にはそのほかにも locate や xargs というユーティリティも入ってます.
インストールが完了すると gfind というコマンドが使えるようになります.
find ではなく gfind なのは macOS 標準の BSD find との競合を避けるためです.
もし gfind ではなく find を GNU find として使いたいという場合は, 上記のコマンドの代わりに次のコマンドでインストールします:
brew install findutils --with-default-names
もし既に最初のコマンドで gfind を使えるようにしてしまって, gfind ではなく find に変更されたいという方は, 次のようにするとできます:
brew uninstall findutils
brew install findutils --with-default-names
ちなみに僕は --with-default-names オプションを付け加えて findutils をインストールしたので, find を GNU find コマンドとして使っています.
なのでこれからの説明でも find を GNU find コマンドとして使っていきたいと思います.
ところで macOS 標準の BSD find ではなくどうして GNU find なのかという疑問を持たれた方, 鋭いご指摘ですw.
どうしてそのことを最初に説明しなかったのかと言いますと, 実は僕自身もよくわからないからです. どうして BSD のより GNU の find の方がいいのかという確固たる理由がないので, 納得していただけるような説明をすることができません.
あえて言いますとなんとなくなんです.
ちゃんとした説明ができないのは自分自身の知識が未熟な証拠ですので, そこは申し訳ないと思います.
ただ強いて理由を言わせていただくならば, GNU は Linux の同伴者のようなものなので, Linux にあまり詳しくないにもかかわらず Linux が好きな僕自身はおのずの GNU も好きになるといった感じなのです.
釈然としないと思いますが, とりあえずそのような理由だということでご了承していただけたらありがたいです.
find コマンドの基本的な使い方
GNU find を使えるようになったら早速使っていきたいと思います.
今更ですが find という名前からも想像できますように, find コマンドはファイルやディレクトリを探し出すためのコマンドです.
find コマンドは様々な条件でファイルを検索できるので, 1 コマンドではありますが奥が深く, 内部の挙動を理解しようとするとちょっとややこしかったりするので侮りは禁物ですw.
試しに .txt で終わるファイルをカレントディレクトリの中から探してみたいと思います:
find . -name '*.txt' -type f
こんな感じになるのです.
. はカレントディレクトリを意味し, -name '*.txt' で .txt で終わる名前のものを絞り込み -type f でファイルのみ探し出しています.
GNU find コマンドの構文は次のようになります:
find [-H] [-L] [-P] [-D debugopts] [-Olevel] [starting-point...] [expression]
[-H] [-L] [-P] [-D debugopts] [-Olevel] がオプションで, [starting-point...] が検索するパス, [expression] が式で検索結果を絞り込むテスト, 見つけだしたファイルに対して実行するアクションなどを指定します.
上記コマンドの場合, . が検索するパス, -name '*.txt' と -type f が式となります. もっと言いますとその二つは式の中のでも検索結果を絞り込むのでテストというカテゴリに分類されます.
前の構文をみていただくとお分かりになると思うのですが検索するパスは [starting-point...] と [] で囲まれているのでオプショナルという意味です. つまり検索するパスを指定しなくてもいいということです.
しかも ... となっているので複数指定することもできます.
もし検索するパスを指定しないと find コマンドはカレントディレクトリを検索するパスとして検索します.
なので上記のコマンドは . を取り除いて次のように書くこともできます:
find -name '*.txt' -type f
このような構文は GNU の find で使える構文で BSD の find では検索するパスを省略することはできないので注意が必要です.
find コマンドの使用例
とちょっと find コマンドの使い方を説明させていただいたので, いくつかの使い方の例を紹介したいと思います.
これから紹介させていただく例は全てカレントディレクトリ以下のパスから検索しますので, 一つ一つの例で “カレントディレクトリ以下のパスから検索します” という説明を省略させていただきたいと思います.
またカレントディレクトリを指す . は省略できるのですが, よりわかりやすくするためにあえて . を指定したいと思います.
“a” または “A” で始まるファイル, ディレクトリを検索する:
find . -iname 'a*'-inameでアルファベットの大文字小文字を区別しないで検索します.空のディレクトリを検索する:
find . -empty -type d-emptyで空のものを,-type dでディレクトリのみ検索します.一週間以上前にアクセスされたファイルを検索する:
find . -atime +6 -type f-atime +6で一週間以上前にアクセスされたものを,-type fでファイルを検索します.100 メガバイトより大きなサイズのファイルを検索する:
find . -size +100M -type f-size +100Mで 100 メガバイトより大きなサイズのものを-type fでファイルを検索します..bakで終わるファイルを削除する:find . -name '*.bak' -type f -delete-name '*.bak'で名前が.bakで終わるものを,-type fでファイルを,-deleteでそれらの条件にマッチするファイルを削除します.-delete削除できるのはファイルと空のディレクトリのみで, ファイルが存在するディレクトリは削除することができません.所有者が
userのファイル, ディレクトリを検索する:find . -user usergroupというグループによって所有されているファイル, ディレクトリを検索する:find . -group group
時間で検索する
GNU find でファイルを検索するときに, 何日以上前にアクセスされたファイルや, 何日未満前に変更されたファイルを見つけたいときがあると思います.
そんなときに時間で検索する方法を覚えておくと便利です.
時間で検索する式は次のようなものがあります.
| 式 | 説明 |
|---|---|
-amin | アクセスされた時間 (分) |
-atime | アクセスされた時間 (日) |
-cmin | メタ情報が変更された時間 (分) |
-ctime | メタ情報が変更された時間 (日) |
-mmin | 変更された時間 (分) |
-mtime | 変更された時間 (日) |
それぞれの引数として数値 n を渡すことができます.
-amin 60 というように.
この場合は 60 分前にアクセスされたものという意味になります.
-atime 7 なら 7 日前のように.
このように n を渡すと何分前, 何日前という意味になります.
また n には + や - を前につけることができます.
+n は n より大きい, -n は n より小さいという意味になり, 符号が付かないそのままの n は n そのままという意味になります.
なので -amin +10 は 10 分前より前にアクセスされた, -amin -10 は 10 分前より後にアクセスされたという意味になります.
ただ -[acm]min の場合は比較的わかりやすいのですが, -[acm]time の場合はすこしややこしいので注意が必要です.
-atime +n だと n + 1 日以上前にアクセスされた, -atime -n だと n 日未満にアクセスされたという意味になります.
つまりは前者は 48 時間以上前にアクセスされた, 後者は 24 時間未満前にアクセスされたという意味になります.
-atime n も n 日前という意味なのですが, もっと正確に言いますと n 日以上前, n 日未満前にアクセスされたという意味になります.
つまり n * 24 時間以上前, (n + 1) * 24 時間未満前にアクセスされたということになります.
少しややこしいですが厳密にはそのようになります.
Examples
10 分前より前にアクセスされたファイルを検索する:
find . -amin +10 -type f10 分前より後にアクセスされたファイルを検索する.
find . -amin -10 -type f10 分前にアクセスされたファイルを検索する:
find . -amin 10 -type f8 日以上前にアクセスされたファイルを検索する:
find . -atime +7 -type f7 日未満前にアクセスされたファイルを検索する:
find . -atime -7 -type f7 日以上前, 8 日未満前にアクセスされたファイルを検索する:
find . -atime 7 -type f
サイズで検索する
-size という式のテストを使うと指定のファイルサイズで検索することができます.
時間で検索するときのように引数として n をとります.
-size n とすると n プロックのサイズのファイルを指定します.
1 ブロック 512 キロバイトなので -size 100 とすると 50 キロバイトのファイルサイズを指定することになります.
検索対象のファイルのサイズは四捨五入されるので, 正確に -size n で指定したサイズでなくてはならないということはありません.
また同じように n には + や - を前につけることができます.
+n なら n サイズより大きい, -n なら n サイズより小さいという意味になります.
+ は以上ではなく大きい, - は以下ではなく小さいという意味になりますので.
加えて -size n の n には単位と呼ばれるものを n の後に付け加えることができます.
それぞれ次のようなものになります:
| 単位 | 説明 |
|---|---|
b | 512 キロバイトのブロック (デフォルト) |
c | バイト |
w | 2 バイトのワード数 |
k | キロバイト |
M | メガバイト |
G | ギガバイト |
Examples
100 バイトより大きいサイズのファイルを検索する:
find . -size +100c -type f2 バイトのワード 100 文字を超えるファイルを検索する:
find . -size +100w -type f100 キロバイトより大きいサイズのファイルを検索する:
find . -size +100k -type f10 メガバイトより大きいサイズのファイルを検索する:
find . -size +10M -type f1 ギガバイトより大きいサイズのファイルを検索する:
find . -size +1G -type f1 ギガバイトより大きく, 10 ギガバイトより小さいファイルを検索する:
find . -size +1G -size -10G -type f
パーミッションで検索する
-perm という式のテストを使うとファイルのパーミッションで検索することができます.
パーミッション 644 と全く同じファイルを検索したい場合:
find . -perm 644 -type f
644 のような数字による表記に加え, シンボルによる表記もできます:
find . -perm u+rw,g+r,o+r -type f
このようにも書けます:
find . -perm u=rw,g=r,o=r -type f
シンボル表記で使われる + や = は -perm の引数の一部として使われる場合は同じ意味です.
-perm で指定するパーミッションには接頭辞として - や / をつけることができます.
-perm -<mode> となると, <mode> のパーミッションが最低限設定されたファイルを検索します.
なので -perm -644 と指定された場合, 644 に加えて 744, 654, 645 などのファイルも検索します.
-perm /<mode> となると, <mode> のパーミッションのユーザ, グループ, その他のそれぞれのどれか一つにでも, ファイルのバーミッションの一部が含まれていれば, そのファイルは検索されます.
例えば -perm /644 となると 600, 040, 004, 700, 050, 005, 400, 200 などのファイルが検索されます.
Examples
少なくとも
755に当てはまるファイルを検索する:find . -perm -755 -type fユーザの
4,2,1, グループの4,1, その他の4,1の計 7 つのパーミッションのうちどれか 1 つでも含んでいるファイルを検索する:
find . -perm /755 -type f
外部コマンドと一緒に使う
-exec という式を使うと外部のコマンドを find コマンドで検索したパスに対して実行することができるようになります.
なので -exec は find コマンドの使い道の幅をグッと広げてくれるとても便利で強力なものです.
.txt で終わるファイルのパスと内容を表示する場合, このようにできます:
find . -name '*.txt' -type f -print -exec cat {} \;
*.txt で .txt で終わる名前, -type f でファイルを, -print で見つかったファイルのパスを表示し, -exec で cat というファイル内容を表示するコマンドを呼び出して {} に find コマンドで見つけたファイルのパスを展開し, その cat コマンドの第一引数として渡しています.
最後に \; を付け足し -exec の終わりを示しています. ; のままだとシェルに特別な文字として認識されてしまうので, バックスラッシュを前に付けてエスケープする必要があります.
';' や ";" のようにクオートで囲ってもエスケープできますが, \; の方が一文字抑えることができるのでおすすめです.
-exec は式の中のアクションというものに分類されます. 式の中で -exec のようなアクションを実行すると, 通常暗黙的に実行されるもう一つのアクション -print が実行されなくなります.
なので今回はファイルのパスを表示するために通常暗黙的に実行される -print というアクションを指定する必要がありました.
という感じで -exec のポテンシャルを感じていただけたのではないでしょうか.
シェルで使うことができるあらゆるコマンドをこの -exec で呼び出して, その呼び出したコマンドの引数として, 検索したパスを {} に展開して渡すことができます.
また -ok という -exec と同じように外部のコマンドを呼び出すことができるアクションがあるのですが, -exec との違いは, 外部のコマンドを実行する前に確認をするということです.
なので確認をしてから実行をしたいコマンドを find コマンドを通じて実行したいような時は, -exec よりも -ok を指定することができます.
Examples
.txtで終わるファイルの情報を表示する:find . -name '*.txt' -type f -exec ls -l {} \;最後にアクセスされたのが 100 日以上前になる全てのファイル,ディレクトリをそれぞれ確認した上で削除する:
find . -atime +99 -ok rm -rf {} \;
オペレータを使う
オペレータと呼ばれるものを式の中で使うと, より複雑な条件で式を作ることができるようになります.
オペレータはそれぞれ次のようなものがあります:
| オペレータ | 説明 |
|---|---|
\( expression \) | 括弧内の式が真なら真. |
\! expression | 式が偽なら真. |
-a | 論理 AND. |
-o | 論理 OR. |
-a や -o が二つの式の間に指定されなかった場合, 暗黙的に -a が使われます.
なので左辺の式が false なら右辺の式は評価されることなくトータルで false と評価され検索がマッチすることはありません.
なので次のような場合:
find . -name '*.txt' -type f
暗黙的に次のように解釈されます:
find . -name '*.txt' -a -type f
論理 AND なのでこの式に検索がマッチするようなものは, 名前が .txt で終わり, かつディレクトリではなくファイルである必要があります.
もし -o を使うと:
find . -name '*.txt' -o -type f
論理 OR なので名前が .txt で終わるか, もしくはファイルであれば検索がマッチすることになります.
こうなってくると, もはやコマンドというよりなんか小さなプログラムを書いているような気になってきます.
Examples
100 メガバイトより小さくなく, 最後にアクセスされたのが 100 日以上前ではないファイルを検索する:
find . ! \( -size -100M -atime +99 \) -type f100 メガバイトより小さいか, もしくは最後にアクセスされたのが 100 日以上前のファイルを検索する:
find . \( -size -100M -o -atime +99 \) -type f
まとめ
いかがでしたでしょうか.
ということで find コマンドの使い方を紹介されていただきました.
散らかった文な上に, やたら長くなってしまって申し訳ないです.
これから少しずつ, わかりやすくお伝えできるようにスキルを磨いていく所存でありますので, お許しいただけるとありがたいです.
ですが find コマンドを使うと結構いろいろなことができる, ということをお知りになっていただけたのではないでしょうか.
個人的には -atime の書き方がややこしいと思っているので, その説明をすることができてよかったです.
あと -exec を使った外部コマンドとの組み合わせ技は強力なので, 少なくともその有用さだけでもお伝えすることが出来たのなら, 個人的にはこの記事を書かせていただいたかいがあるってもんです.
より詳しい情報をお求めの方はおきまりの man find で文字の暗黒世界へ踏み入っていただけたらと思います.
Homebrew の --with-default-names オプションを付けないで findutils をインストールされた方は man gfind となりますね.
最後までお読みくださいましてありがとうございました.
関連記事
Mac で使える Emacs ライクなショートカットが便利2018.02.06
Ruby の正規表現を備忘録としてまとめてみた2018.08.30
Homebrew で macOS に GNU コマンドをインストールする2018.07.25
macOS High Sierra の三本指タップで辞書を引くのが便利2018.06.16
macOS で Mouse Keys を使ってキーボードでポインタを操作する方法2018.07.11