Linuxのfindコマンド:-name、-type、-size、-mtimeオプションと-delete
Linuxのfindコマンドは、開始フォルダーの下を順にたどり、名前や種類、サイズ、更新時刻、権限などの条件に合うファイルとフォルダーを探すコマンドです。AlmaLinux 9サーバーのテストフォルダーにファイルを10個作り、条件を1つずつ変えて実行した結果、見つけたファイルを-execで別のコマンドに渡し、削除するときは-deleteの前に同じ条件で一覧から確認するようになりました。
結果欄のテストフォルダーのパスとファイルの時刻は例の値に置き換えました。Debian 12サーバーの出力はホームフォルダーのパスだけが例の値で、残りはそのまま載せています。
1️⃣ Linuxのfindコマンドの使い方
基本の形は[find 開始パス 条件]です。開始パスを省くと今のフォルダー(.)で探し、条件を省くと開始パスの下のファイルとフォルダーをすべて出力します。
テストは以下のようにフォルダー3つとファイル10個を作った/root/workで行いました。
find /root/work/root/work
/root/work/docs
/root/work/docs/readme.txt
/root/work/docs/guide.txt
/root/work/docs/old.tmp
/root/work/logs
/root/work/logs/app.log
/root/work/logs/cache.tmp
/root/work/backup
/root/work/report.txt
/root/work/README.md
/root/work/run.sh
/root/work/my notes.txt
/root/work/empty.txtfindは結果を名前順に並べ替えません。今回の実行では、docsフォルダーに達するとその中のファイルを先にすべて出力してからlogsへ移りました。
条件を複数続けて書くと、すべてに一致するファイルだけが残ります。
-nameはパスではなくファイル名だけをパターンと比較します。パターンをシングルクォートで囲むと、シェルが*を先に展開せず、そのままfindに渡します。
find /root/work -name '*.txt'/root/work/docs/readme.txt
/root/work/docs/guide.txt
/root/work/report.txt
/root/work/my notes.txt
/root/work/empty.txt-nameは大文字と小文字を区別します。readmeで始まる名前を探すとdocs/readme.txtだけが見つかり、-inameに変えるとREADME.mdも一緒に見つかりました。
find /root/work -name 'readme*'/root/work/docs/readme.txtfind /root/work -iname 'readme*'/root/work/docs/readme.txt
/root/work/README.mdfindはfindutilsパッケージに入っています。[find --version]の1行目で確認したバージョンは、AlmaLinux 9.8サーバーがfindutils 4.8、Debian 12サーバーが4.9でした。
2️⃣ find -type dと-maxdepth
-type fは通常のファイルだけを、-type dはフォルダーだけを残します。
find /root/work -type d/root/work
/root/work/docs
/root/work/logs
/root/work/backup開始パスの/root/workもフォルダーなので、1行目に一緒に表示されました。同じパスで-type fに変えると、ファイル10個がすべて表示されました。
-maxdepthは開始パスから何階層下まで進むかを決めます。-maxdepth 1で実行すると、docsとlogsの中のファイルは外れ、/root/workのすぐ下のファイル5個だけが残りました。
find /root/work -maxdepth 1 -type f/root/work/report.txt
/root/work/README.md
/root/work/run.sh
/root/work/my notes.txt
/root/work/empty.txt逆に-mindepth 2は開始パスのすぐ下を飛ばし、サブフォルダーの中のファイル5個だけを出力しました。
find /root/work -mindepth 2 -type f/root/work/docs/readme.txt
/root/work/docs/guide.txt
/root/work/docs/old.tmp
/root/work/logs/app.log
/root/work/logs/cache.tmpmanページは、-maxdepthのようなグローバルオプションを開始パスのすぐ後に書くよう案内しています。-type fの後に-maxdepth 1を書いても結果は同じ5ファイルで、ターミナルで実行するとグローバルオプションを前に書くよう促す警告が1行表示されます(manページによる)。
フォルダーやファイルの数だけを確認するときは、出力をwc -lに渡します。-type dの出力を数えると、開始パスを含めて4でした。
find /root/work -type d | wc -l4-maxdepth 1を付けても4でした。サブフォルダー3つがすべて開始パスのすぐ下にあるためです。
find /root/work -maxdepth 1 -type d | wc -l4-mindepth 2でサブフォルダーの中のファイルだけを数えると5でした。
find /root/work -mindepth 2 -type f | wc -l53️⃣ findコマンドのサイズ・時間の条件
数字の前の+はその値より大きい、-はその値より小さいことを意味し、符号がなければちょうどその値です。-sizeの単位は以下の表のとおりです(manページによる)。
| 単位 | サイズ | はい |
|---|---|---|
| c | 1バイト | -size +500c |
| k | 1,024バイト | -size +1k |
| M | 1,048,576バイト | -size +1M |
| G | 1,073,741,824バイト | -size +1G |
| (なし) | 512バイトのブロック | -size +2 |
find /root/work -type f -size +1k/root/work/docs/guide.txt
/root/work/logs/app.log
/root/work/report.txt1kより大きいファイルは、5,000バイトのguide.txt、2MBのapp.log、1,200バイトのreport.txtでした。
find /root/work -size +1M/root/work/logs/app.log-sizeを2回続けて範囲を決めると、1kより大きく100kより小さい2つのファイルだけが残りました。
find /root/work -type f -size +1k -size -100k/root/work/docs/guide.txt
/root/work/report.txt-sizeはサイズを単位で割って切り上げてから比較します。そのため300バイトのREADME.mdも1kとして計算され、-size -1kにはサイズ0のempty.txtだけが表示されました。
find /root/work -type f -size -1k/root/work/empty.txt時間の条件を見るために、README.md、my notes.txt、docs/readme.txtは3日前に、.tmpファイル2つは10日前に更新時刻を合わせておきました。ファイルごとにアクセス、更新、変更の3つの時刻が記録され、 statコマンドで確認できます。
-mtimeは最後に更新してからの経過時間を24時間単位で数えます。-mtime -1には24時間以内に作ったファイル5個が表示されました。
find /root/work -type f -mtime -1/root/work/docs/guide.txt
/root/work/logs/app.log
/root/work/report.txt
/root/work/run.sh
/root/work/empty.txt-mtime +7には10日前に合わせた.tmpファイル2つだけが表示されました。古いログや一時ファイルを削除する前に一覧を確認するときにこの形を使います。
find /root/work -type f -mtime +7/root/work/docs/old.tmp
/root/work/logs/cache.tmp-mminは同じ計算を分単位で行います。-mmin -60で実行した結果は、-mtime -1と同じ5ファイルでした。
find /root/work -type f -mmin -60/root/work/docs/guide.txt
/root/work/logs/app.log
/root/work/report.txt
/root/work/run.sh
/root/work/empty.txt-newerは基準のファイルより後に更新したファイルを探します。3日前に合わせたREADME.mdを基準に実行すると、その後に作ったファイル5個が表示されました。
find /root/work -type f -newer /root/work/README.md/root/work/docs/guide.txt
/root/work/logs/app.log
/root/work/report.txt
/root/work/run.sh
/root/work/empty.txt-emptyはサイズ0のファイルと中身が空のフォルダーを一緒に探し、空のフォルダーbackupとempty.txtが表示されました。
find /root/work -empty/root/work/backup
/root/work/empty.txt4️⃣ find -permと-userの条件
-permの後に数字だけを書くと、権限がちょうどその値のファイルを探します。-perm 644で実行すると、755のrun.shと600のmy notes.txtは外れ、8個が表示されました。
find /root/work -type f -perm 644/root/work/docs/readme.txt
/root/work/docs/guide.txt
/root/work/docs/old.tmp
/root/work/logs/app.log
/root/work/logs/cache.tmp
/root/work/report.txt
/root/work/README.md
/root/work/empty.txtモードの前に-を付けると、書いた権限がすべて含まれるファイルを探します。所有者の実行権限(u+x)があるファイルはrun.shの1つでした。
find /root/work -type f -perm -u+x/root/work/run.shモードの前に/を付けると、書いた権限のうち1つでもあれば見つかります。6000はSUID(4000)とSGID(2000)を足した値で、実行した瞬間に所有者やグループの権限で動作するファイルをサーバーで点検するときにこの形を使います。
テストフォルダーには該当するファイルがなかったので、run.shにSUIDを付けて実行すると、run.shの1つが表示されました。
chmod u+s /root/work/run.shfind /root/work -type f -perm /6000/root/work/run.sh古い資料には同じ位置に-perm +6000と書かれています。そのまま実行すると[invalid mode]エラーで終わりました。
find /root/work -type f -perm +6000find: invalid mode '+6000'+の形はfindutils 4.5.12で削除されたので(manページによる)、/で書きます。テスト用に付けたSUIDはchmod u-sで外しました。
-userは所有者名で絞り込みます。テストファイルはすべてroot所有なので-user rootだけでは絞り込めず、-nameで.logファイルも一緒に選ぶとapp.logの1つが表示されました。
find /root/work -type f -user root -name '*.log'/root/work/logs/app.log-nouserは所有者のUIDに対応するアカウントがないファイルを探します。アカウントを削除した後にサーバーに残ったファイルを点検するときに使い、guide.txtの所有者を存在しないUIDの1500に変えて実行すると、guide.txtの1つが表示されました。
chown 1500:1500 /root/work/docs/guide.txtfind /root/work -nouser/root/work/docs/guide.txt-nogroupは同じ確認を所有グループで行い、グループも1500に変えたguide.txtが表示されました。テストの後はchown root:rootで元に戻しました。
条件の間に-oを入れると、どちらか一方に一致すれば残ります。-type fのような別の条件と一緒に使うときは、-oでつなぐ条件をかっこで囲みます。かっこはシェルが先に解釈しないよう前にバックスラッシュを付け、かっこと条件の間は空けて書きます。
find /root/work -type f \( -name '*.log' -o -name '*.tmp' \)/root/work/docs/old.tmp
/root/work/logs/app.log
/root/work/logs/cache.tmp!はすぐ後の条件を反転します。.txtではないファイルを探すと5個が表示されました。
find /root/work -type f ! -name '*.txt'/root/work/docs/old.tmp
/root/work/logs/app.log
/root/work/logs/cache.tmp
/root/work/README.md
/root/work/run.sh5️⃣ find -execの活用
-execは見つけたファイルごとに後ろに書いたコマンドを実行し、{}の位置に見つけたパスが入ります。コマンドの末尾の;は、シェルがコマンドの区切り記号として先に読まないよう\;と書きます。
find /root/work -name '*.log' -exec ls -l {} \;-rw-r--r-- 1 root root 2097152 Oct 5 14:10 /root/work/logs/app.log末尾を\;の代わりに+にすると、見つけたパスをまとめてコマンドを1回だけ実行します。echoで比べると、\;はパス5つを5行で、+は1行で出力しました。
find /root/work -name '*.txt' -exec echo {} \;/root/work/docs/readme.txt
/root/work/docs/guide.txt
/root/work/report.txt
/root/work/my notes.txt
/root/work/empty.txtfind /root/work -name '*.txt' -exec echo {} +/root/work/docs/readme.txt /root/work/docs/guide.txt /root/work/report.txt /root/work/my notes.txt /root/work/empty.txtlsのように複数のパスを受け取るコマンドは、+でまとめて実行回数を減らします。1kより大きいファイル3つのサイズを1回のls -lhで確認しました。
find /root/work -type f -size +1k -exec ls -lh {} +-rw-r--r-- 1 root root 4.9K Oct 5 14:10 /root/work/docs/guide.txt
-rw-r--r-- 1 root root 2.0M Oct 5 14:10 /root/work/logs/app.log
-rw-r--r-- 1 root root 1.2K Oct 5 14:10 /root/work/report.txtWebフォルダーの権限をファイル644、フォルダー755に一括で整えるときも-execを使います。実行前に値が異なるファイルから探すと、run.shとmy notes.txtが表示されました。
find /root/work -type f ! -perm 644/root/work/run.sh
/root/work/my notes.txtファイルとフォルダーは権限が異なるので、-type fと-type dに分けて実行します。chmodも複数のパスを受け取るコマンドなので、末尾を+にしました。
find /root/work -type f -exec chmod 644 {} +find /root/work -type d -exec chmod 755 {} +同じ条件で再び探すと何も表示されず、事前に700に変えておいたbackupフォルダーも755になりました。
ただしrun.shも644に変わり、実行すると[Permission denied]が表示されました。実行ファイルが混ざったフォルダーでは、それらのファイルの実行権限をchmod +xで付け直します。
サブフォルダーの中のファイルだけを変えるときは-mindepth 2を付けます。660で実行するとdocsとlogsの中の5ファイルだけが変わり、確認後に644へ戻しました。
find /root/work -mindepth 2 -type f -exec chmod 660 {} +find /root/work -type f -perm 660/root/work/docs/readme.txt
/root/work/docs/guide.txt
/root/work/docs/old.tmp
/root/work/logs/app.log
/root/work/logs/cache.tmpfindとgrepをつなぐと、名前ではなくファイルの内容で探せます。Webシェルを点検する状況を作るため、別のフォルダー/root/siteにPHPファイルを4つ作り、uploadフォルダーのhtml-cache.phpとphoto.phpに[base64_decode]という文字列を入れておきました。
find /root/site -type f/root/site/lib/func.php
/root/site/upload/html-cache.php
/root/site/upload/photo.php
/root/site/index.phpWebシェルの名前がわかっていれば-nameで探します。アップロードフォルダーまでだけ進むよう-maxdepth 2を付けると、html-cache.phpの1つが表示されました。
find /root/site -maxdepth 2 -type f -name 'html*.php'/root/site/upload/html-cache.php名前を平凡に変えたphoto.phpはここには表示されませんでした。-execでgrep -lを実行すると文字列を含むファイルのパスだけを出力し、2つのファイルがどちらも表示されました。
find /root/site -name '*.php' -exec grep -l 'base64_decode' {} +/root/site/upload/html-cache.php
/root/site/upload/photo.php[base64_decode]はWordPressのコアファイルにも含まれる関数なので、見つかったファイルは内容を開いて確認してから削除します。
-print0はパスの間に改行の代わりにヌル文字を入れます。ヌル文字は画面に見えないので、パスが1行につながって表示されました。
find /root/work -name '*.txt' -print0/root/work/docs/readme.txt/root/work/docs/guide.txt/root/work/report.txt/root/work/my notes.txt/root/work/empty.txtmy notes.txtのように空白を含む名前も途切れずに別のコマンドへ渡すときは、-print0の出力を xargs -0と組み合わせて使います(manページによる)。
6️⃣ findコマンドでのファイル削除とオプション
削除する前には、同じ条件を削除なしで先に実行し、削除するファイルの一覧を目で確認します。
find /root/work -name '*.tmp'/root/work/docs/old.tmp
/root/work/logs/cache.tmp-exec rm {} \;は見つけたファイルごとにrmを実行します。logsフォルダーだけで実行すると、cache.tmpが削除されてapp.logだけが残りました。
find /root/work/logs -name '*.tmp' -exec rm {} \;ls /root/work/logsapp.log-deleteはrmを別に実行せず、findが直接削除します。実行しても何も出力されず、同じ条件で再び探すと.tmpファイルは残っていませんでした。
find /root/work -name '*.tmp' -deletefind /root/work -name '*.tmp'-deleteは必ず条件の後に書きます。findはコマンド行を前から1つの式として評価するので、-deleteを先頭に置くと、条件を見る前に開始パスの下をすべて削除しようとします(manページによる)。
🔲 findコマンドのオプション
📌 主なオプション: この記事で実行した結果
| オプション | 作業 | この記事で確認した結果 | |
|---|---|---|---|
| -name | 名前がパターンに一致するものを探す(大文字と小文字を区別) | '*.txt'で5ファイル、'readme*'でdocs/readme.txtの1つだけ表示 | |
| -iname | 大文字と小文字を区別せずに名前を比較 | 'readme*'でdocs/readme.txtとREADME.mdが一緒に表示 | |
| -type f, -type d | 通常のファイル(f)またはフォルダー(d)だけを残す | -type dで開始パスを含むフォルダー4つ、-type fでファイル10個が表示 | |
| -maxdepth | 開始パスからN階層下までだけ進む | -maxdepth 1で/root/workのすぐ下のファイル5個だけが残る | |
| -mindepth | 開始パスからN階層より上を飛ばす | -mindepth 2でサブフォルダーの中のファイル5個だけが表示 | |
| -size | サイズで探す(+は大きい、-は小さい、単位c、k、M、G) | -size +1kで3ファイル、-size -1kでempty.txtの1つだけが表示 | |
| -mtime | 内容を更新してからの経過時間を24時間単位で比較 | -mtime -1で5ファイル、-mtime +7で.tmpファイル2つが表示 | |
| -mmin | 更新してからの経過時間を分単位で比較 | -mmin -60で-mtime -1と同じ5ファイルが表示 | |
| -ctime | 権限や名前などのファイル情報が変わった時刻まで24時間単位で比較 | -ctime -1でテストファイル10個がすべて表示 | |
| -newer | 基準のファイルより後に更新したものを探す | 3日前に合わせたREADME.mdを基準に5ファイルが表示 | |
| -empty | サイズ0のファイルと空のフォルダーを探す | 空のフォルダーbackupとempty.txtが表示 | |
| -perm | 権限で探す(数字だけならちょうどその値、前に-を付けると書いた権限がすべて含まれるもの、/を付けると1つでも含まれるもの) | -perm 644で8ファイル、-perm -u+xでrun.shの1つ、SUIDを付けた後-perm /6000でrun.shが表示 | |
| -user | 所有者名で絞り込む | -name '*.log'と一緒に書いてapp.logの1つが表示、存在しないアカウントはエラーで終了 | |
| -nouser | 所有者のUIDに対応するアカウントがないものを探す | UIDを1500に変えたguide.txtの1つが表示 | |
| -nogroup | 所有グループのGIDに対応するグループがないものを探す | GIDを1500に変えたguide.txtの1つが表示 | |
| -o | どちらか一方に一致すれば残す(別の条件と一緒に使うときはかっこで囲む) | .logと.tmpをまとめて3ファイルが表示 | |
| ! | すぐ後の条件を反転 | .txtではないファイル5個が表示 | |
| -exec | 見つけたファイルごとに後ろに書いたコマンドを実行({}の位置にパス) | 末尾を+にするとecho 1回で5つのパスが1行で表示 | |
| -print0 | パスの間に改行の代わりにヌル文字を入れる | ヌル文字が見えず、パスが1行につながる | |
| -delete | 見つけたファイルをfindが直接削除(条件の後に書く) | 何も出力されず、再び探すと.tmpファイルなし |
📎 その他のオプション: 公式マニュアルより
| オプション | 作業 | |
|---|---|---|
| -L | シンボリックリンクをたどり、リンク先ファイルの情報を使う | |
| -path | 名前の代わりにパス全体をシェルのパターンと比較 | |
| -regex | パス全体を正規表現と比較 | |
| -iregex | -regexのように比較するが大文字と小文字を区別しない | |
| -atime | 最後にアクセスしてからの経過時間を24時間単位で比較 | |
| -cnewer | 状態が変わった時刻が基準ファイルの更新時刻より後のものを探す | |
| -group | 所有グループ名で絞り込む | |
| -xdev | 別のファイルシステムのフォルダーへ進まない | |
| -daystart | 時間の条件を24時間前ではなく今日の0時から計算 | |
| -prune | フォルダーならその下へ進まない | |
| -quit | すぐ終了(-print -quitで最初の結果だけを出力) | |
| -printf | 出力の形を自分で決めて出力 | |
| -ls | 見つけたファイルをls -dilsの形で出力 | |
| -fprint | 見つけたパスを画面ではなく指定のファイルに保存 | |
| -ok | -execのように実行するが、実行するかを先に聞く | |
| -execdir | -execのように実行するが、見つけたファイルのあるフォルダーでコマンドを実行 |
🔲 findコマンドシミュレーター
以下のシミュレーターは同じテストフォルダーを入れた仮想サーバーで、-name、-type、-size、-mtime、-permなどの条件と-exec、-deleteを実際に実行できます。
7️⃣ findコマンドのエラー
パターンをクォートなしで書くと、シェルが ワイルドカード の規則に従って今のフォルダーのファイル名に先に展開します。docsフォルダーへ移動して*.txtをクォートなしで書くと、シェルがguide.txtとreadme.txtの2つの名前に変えて渡し、以下のエラーが表示されました。
cd /root/work/docsfind /root/work -name *.txtfind: paths must precede expression: `readme.txt'
find: possible unquoted pattern after predicate `-name'?終了コードは1でした。
-execの末尾に\;や+を付け忘れると[missing argument to `-exec’]が表示されました。
find /root/work -name '*.log' -exec ls -l {}find: missing argument to `-exec'存在しないパスを開始パスに書くと[No such file or directory]が表示され、終了コード1で終わりました。
find /nonexistent-dir -name xfind: '/nonexistent-dir': No such file or directory-userに存在しないアカウント名を書いたり、-typeにfやdのような決まった文字以外の値を書いたりしても、検索を始めずにすぐエラーで終わりました。
find /root/work -user nosuchuserfind: 'nosuchuser' is not the name of a known userfind /root/work -type xfind: Unknown argument to -type: xDebian 12サーバーの一般アカウントで、テストフォルダー内のlockフォルダーの権限を000に変えてからテストフォルダーを検索しました。lockの中を読めず[Permission denied]が表示され、終了コードは1でした。
find ~/findtest/home/user/findtest
/home/user/findtest/report.txt
/home/user/findtest/lock
find: '/home/user/findtest/lock': Permission denied
/home/user/findtest/docs
/home/user/findtest/docs/readme.txtエラーの行だけを隠すには2>/dev/nullを付けます。エラーの文言は消えましたが、終了コードは1のままでした。
find ~/findtest -name '*.txt' 2>/dev/null/home/user/findtest/report.txt
/home/user/findtest/docs/readme.txt🔢 FAQ & おすすめコンテンツ
ℹ️ 提携について
当サイトのコンテンツにはアフィリエイトリンクが含まれています。訪問者がこのリンクを通じて商品やサービスを購入すると、当サイトは販売元から手数料を受け取ります。この過程において、購入者が支払う金額(イベント割引適用で金額が下がります↓)は上昇しません。掲載されている価格・割引・在庫情報は作成時点のものであり、実際とは異なる場合がありますので、ご購入前に販売元にて最終確認をお願いいたします。商品の選定および評価は独自の基準に基づいて行われており、手数料の支払いの有無は紹介順や評価内容に影響を与えません。