- 技術 💻
findコマンドの使い方|目的別逆引き40例と-exec・xargsの違い
findコマンドとは、フォルダの中を下の階層までたどりながら、名前・種類・サイズ・更新日時などの条件に合うファイルを探し、見つけたファイルに対して削除や別のコマンドの実行までできるコマンドです。「1GBを超えるファイルはどこ?」「7日より古いログを消したい」「node_modules を除いて .js を探したい」といった作業を1行で行えます。
この記事では、名前・サイズ・更新日時を調整したダミーのファイル群を作り、すべてのコマンドを実際に実行して、その出力をそのまま載せています(GNU findutils 4.9.0、Ubuntu 24.04)。特につまずきやすい -mtime の +と- の意味、-size の切り上げ、-exec の \; と + の実行回数と速度、空白や改行を含むファイル名で xargs がどう壊れるかを実測で確認し、GNU find と macOS(BSD)の find の違いも公式マニュアルをもとに表にまとめました。
findコマンドの早見表(目的別)
| やりたいこと | コマンド |
|---|---|
| 名前で探す | find . -name 'app.js' |
| 拡張子で探す | find . -name '*.log' |
| 大文字・小文字を区別しない | find . -iname '*.jpg' |
| ファイルだけ / フォルダだけ | find . -type f / find . -type d |
| 100MBより大きい | find . -type f -size +100M |
| 大きい順に上位10件 | find . -type f -printf '%s\t%p\n' | sort -nr | head |
| 24時間以内に更新 | find . -mtime -1 / find . -mmin -1440 |
| 7日より前(8日以上前)に更新 | find . -mtime +7 |
| 特定の日付より後に更新 | find . -newermt '2026-09-01' |
| 空のファイル・フォルダ | find . -empty |
| 権限が777のファイル | find . -type f -perm 777 |
| 所有者で探す | find . -user www-data |
| 深さを限定 | find . -maxdepth 2 |
| フォルダを除外 | find . -name node_modules -prune -o -name '*.js' -print |
| OR 条件 | find . \( -name '*.md' -o -name '*.pdf' \) |
| 否定 | find . -not -name '*.log' |
| 見つけたものを削除 | find logs -name '*.log' -mtime +7 -delete |
| まとめてコマンド実行 | find . -name '*.log' -exec gzip {} + |
| 空白入りの名前も安全に xargs へ | find . -print0 | xargs -0 wc -l |
| 中身も検索(grep と組み合わせ) | find . -name '*.md' -exec grep -l TODO {} + |
| 1件見つけたら終了 | find . -name '*.js' -print -quit |
| 権限エラーを隠す | find / -name x 2>/dev/null |
基本の形は find 探す場所 条件 動作 です。条件を並べると AND、-o で OR、動作を省略すると -print(パスを表示)になります。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「findコマンドの使い方|目的別逆引き40例と-exec・xargsの違い」
検証用のファイルを作る
記事と同じ結果を確かめられるよう、ダミーのファイル群を作るコマンドを載せます(GNU coreutils の touch -d を使っています)。
mkdir -p proj/{src,docs,logs,node_modules/lib,build,.git,empty_dir} && cd proj
printf 'console.log(1)\n// TODO: fix\n' > src/app.js
printf 'export const a=1\n' > src/util.ts
printf 'x\n' > src/App.JS
: > src/empty.txt
printf '# README\nTODO: write\n' > docs/README.md
printf 'guide\n' > docs/guide.md
head -c 3000000 /dev/zero > build/bundle.js # 約2.9MB
head -c 1200000 /dev/zero > docs/manual.pdf # 約1.2MB
head -c 1024 /dev/zero > docs/one_k.bin
head -c 500 /dev/zero > docs/small.bin
printf 'dep\n' > node_modules/lib/index.js
printf 'ref\n' > .git/HEAD
now=$(date +%s)
for h in 12 36 60 84 180 400; do # 12〜400時間前に更新したログ
printf 'INFO ok\nERROR fail\n' > logs/app_${h}h.log
touch -d "@$((now - h*3600))" logs/app_${h}h.log
done
printf 'a\n' > 'logs/my report.log' # 空白入りの名前
printf 'b\n' > $'logs/line\nbreak.log' # 改行入りの名前
chmod 777 src/util.ts; chmod 600 docs/guide.md
find の出力順はファイルシステム次第で毎回同じとは限らないため、この記事では多くの例で | sort を付けて並べています。
名前・種類で探す
1. 拡張子で探す
$ find . -name '*.js' | sort
./build/bundle.js
./node_modules/lib/index.js
./src/app.js
2. 大文字・小文字を区別せずに探す(-iname)
$ find . -iname '*.js' | sort
./build/bundle.js
./node_modules/lib/index.js
./src/App.JS
./src/app.js
3. パターンは必ず引用符で囲む
引用符がないと、シェルが先に *.js を今いるフォルダのファイル名に置き換えます。a.js と b.js があるフォルダで試しました。
$ find . -name *.js
find: paths must precede expression: `b.js'
find: possible unquoted pattern after predicate `-name'?
$ find . -name '*.js' | sort
./a.js
./b.js
./sub/c.js
今いるフォルダに .js が1つだけだと、エラーにならずに -name a.js として動き、sub/c.js が結果から漏れます。-name '*.js' と引用符で囲めば、どちらの場合も正しく動きます。
4. ファイルだけ・フォルダだけ(-type)
$ find . -type d | sort
.
./.git
./build
./docs
./empty_dir
./logs
./node_modules
./node_modules/lib
./src
| -type | 意味 |
|---|---|
f |
通常のファイル |
d |
ディレクトリ |
l |
シンボリックリンク |
5. パス全体で探す(-path / -regex)
-name はファイル名だけ、-path と -regex はパス全体が対象です。
$ find src -regextype posix-extended -regex '.*/[a-z]+\.(js|ts)' | sort
src/app.js
src/util.ts
$ find src -regex 'app.js'
(出力なし:パス全体 src/app.js と一致しないため)
サイズで探す
6. 1MBより大きいファイル
$ find . -size +1M | sort
./build/bundle.js
./docs/manual.pdf
7. 大きいファイル上位(-printf とsort)
$ find . -type f -printf '%s\t%p\n' | sort -nr | head -3
3000000 ./build/bundle.js
1200000 ./docs/manual.pdf
1024 ./docs/one_k.bin
$ find . -type f -exec du -h {} + | sort -rh | head -3
2.9M ./build/bundle.js
1.2M ./docs/manual.pdf
4.0K ./src/util.ts
-printf '%s' はバイト数、du はディスク上の使用量(ブロック単位)なので、小さなファイルでは値が違って見えます。
8. -size の「切り上げ」に注意
$ find . -size -1M -type f
./src/empty.txt
$ find . -size -1k -type f | sort
./src/empty.txt
$ find . -type f -size +1000k -size -2M
(出力なし)
-size は、サイズを指定した単位に切り上げてから比べます。500バイトのファイルは「1k」、1.2MBのファイルは「2M」とみなされるため、-size -1M は「1M未満=0M」、つまり空のファイルだけに一致します。細かく指定したいときは、バイト単位の c を使います。
$ find docs -size -1024c -type f | sort
docs/README.md
docs/guide.md
docs/small.bin
| 単位 | 意味(GNU find) |
|---|---|
c |
バイト |
k / M / G |
KiB / MiB / GiB(切り上げ) |
| 単位なし | 512バイトのブロック数 |
更新日時で探す
9. -mtime の +と- を実際のファイルで確かめる
更新から12・36・60・84・180・400時間たったログで試しました。
$ find logs -name 'app_*' -mtime 0 | sort
logs/app_12h.log
$ find logs -name 'app_*' -mtime 1 | sort
logs/app_36h.log
$ find logs -name 'app_*' -mtime +1 | sort
logs/app_180h.log
logs/app_400h.log
logs/app_60h.log
logs/app_84h.log
$ find logs -name 'app_*' -mtime -2 | sort
logs/app_12h.log
logs/app_36h.log
$ find logs -name 'app_*' -mtime +7 | sort
logs/app_400h.log
find は「経過時間 ÷ 24時間」の小数点以下を切り捨てた日数で比べます。表にするとこうなります。
| ファイル | 経過 | 日数(切り捨て) | -mtime 1 |
-mtime +1 |
-mtime -2 |
-mtime +7 |
|---|---|---|---|---|---|---|
| app_12h | 0.5日 | 0 | ○ | |||
| app_36h | 1.5日 | 1 | ○ | ○ | ||
| app_60h | 2.5日 | 2 | ○ | |||
| app_84h | 3.5日 | 3 | ○ | |||
| app_180h | 7.5日 | 7 | ○ | |||
| app_400h | 16.7日 | 16 | ○ | ○ |
-mtime +7 は「7日より前」ではなく「日数が7より大きい=8日以上前」です。180時間(7.5日)のファイルは日数7なので含まれません。
| 書き方 | 意味 |
|---|---|
-mtime -1 |
24時間以内 |
-mtime 1 |
24時間以上〜48時間未満 |
-mtime +1 |
48時間以上前 |
-mtime +7 |
8日(192時間)以上前 |
10. 分単位で探す(-mmin)
$ find logs -name 'app_*' -mmin -1440 | sort
logs/app_12h.log
11. 日付の区切りで数える(-daystart)
$ find logs -name 'app_*' -daystart -mtime 1 | sort
logs/app_36h.log
-daystart を付けると「今から24時間前」ではなく「今日の0時」を基準に数えます。
12. 特定の日付より後(-newermt)
$ find logs -newermt '2026-09-26' -name 'app_*' | sort
logs/app_12h.log
logs/app_36h.log
logs/app_60h.log
logs/app_84h.log
13. あるファイルより新しい(-newer)
$ find src -newer src/App.JS | sort
src
src/app.js
src/empty.txt
src/util.ts
「デプロイ後に変更されたファイル」を調べるときは、目印のファイルを touch しておき -newer で比べると便利です。
空・権限・所有者で探す
14〜16. 空のファイル・空のフォルダ
$ find . -empty | sort
./empty_dir
./src/empty.txt
$ find . -type f -empty
./src/empty.txt
$ find . -type d -empty
./empty_dir
17〜19. 権限(-perm)
$ find . -type f -perm 777
./src/util.ts
$ find . -type f -perm -o=w # その他ユーザーに書き込み権限がある
./src/util.ts
$ find docs -type f -perm -g=r | sort # グループに読み取り権限がある
docs/README.md
docs/manual.pdf
docs/one_k.bin
docs/small.bin
| 書き方 | 意味 |
|---|---|
-perm 644 |
権限がちょうど 644 |
-perm -644 |
644 の権限をすべて持つ(それ以上も含む) |
-perm /022 |
どれか1つでも持つ |
20〜21. 所有者(-user / -nouser)
$ find src -user root -type f | sort | head -3
src/App.JS
src/app.js
src/empty.txt
$ find src -nouser
(出力なし:所有者のユーザーが存在しないファイルはない)
深さ・除外・条件の組み合わせ
22. 深さを限定(-maxdepth / -mindepth)
$ find . -maxdepth 1 | sort
.
./.git
./build
./docs
./empty_dir
./logs
./node_modules
./src
$ find . -mindepth 2 -maxdepth 2 -type d
./node_modules/lib
23. フォルダを除外する(-prune)
$ find . -type d -name node_modules -prune -o -name '*.js' -print | sort
./build/bundle.js
./src/app.js
24. 複数のフォルダを除外する
$ find . \( -name node_modules -o -name .git \) -prune -o -type f -name '*.md' -print | sort
./docs/README.md
./docs/guide.md
25. -not -path で除外する
$ find . -name '*.js' -not -path './node_modules/*' | sort
./build/bundle.js
./src/app.js
| 方法 | 動き | 向いている場面 |
|---|---|---|
-prune |
そのフォルダの中に入らない | node_modules など大きいフォルダ(速い) |
-not -path |
中まで調べてから結果から外す | 書きやすさ優先、小さいフォルダ |
26. OR 条件は括弧で囲む
$ find . -name '*.md' -o -name '*.pdf' -print
./docs/manual.pdf
$ find . \( -name '*.md' -o -name '*.pdf' \) -print | sort
./docs/README.md
./docs/guide.md
./docs/manual.pdf
AND(並べる)は OR(-o)より優先されます。1つ目は「-name '*.md'」または「-name '*.pdf' かつ -print」と解釈されるため、.md が表示されません。OR を使うときは \( ... \) で囲むと意図どおりになります(括弧の前後には空白が必要です)。
27. 否定
$ find logs -name '*.log' -not -name 'app_*'
logs/my report.log
logs/line
break.log
最後の2行は、改行を含む1つのファイル名 line⏎break.log です。この扱いは後の「xargs」の節で確かめます。
削除する
28. 7日より古いログを削除(-delete)
削除の例(28〜31)は、元のフォルダをコピーした練習用フォルダで実行しています。
$ find logs -name 'app_*.log' -mtime +7 -print # まず表示して確認
logs/app_400h.log
$ find logs -name 'app_*.log' -mtime +7 -delete
$ ls -b logs
app_12h.log
app_180h.log
app_36h.log
app_60h.log
app_84h.log
line\nbreak.log
my\ report.log
ls -b は、改行や空白を \n \ のように記号で表示するオプションです。
29. -exec rm で削除(削除したものを表示)
$ find logs -name 'app_*.log' -mtime +3 -exec rm -v {} +
removed 'logs/app_180h.log'
30. 空のフォルダを削除
$ find . -type d -empty -delete -print
./empty_dir
31. -delete は必ず条件の後に書く
練習用にコピーしたフォルダで、順番を入れ替えて試しました。
$ find logs -delete -name '*.log'; ls logs
ls: cannot access 'logs': No such file or directory
find は左から順に評価するため、-delete を先に書くと条件を見る前にすべて削除されます。削除の前に、同じ条件で -print だけを実行して対象を確かめる習慣を付けると安心です。
また、-delete と -prune は同時に使えません。
$ find . -maxdepth 1 -name 'x' -prune -delete
find: The -delete action automatically turns on -depth, but -prune does nothing when -depth is in effect. If you want to carry on anyway, just explicitly use the -depth option.
-exec の {} \; と {} + の違い
32〜33. 実行回数の違い
$ find logs -type f -exec sh -c 'echo x' \; | wc -l
8
$ find logs -type f -exec sh -c 'echo x' sh {} + | wc -l
1
| 書き方 | 動き | 8ファイルのとき |
|---|---|---|
-exec コマンド {} \; |
1ファイルごとにコマンドを1回実行 | 8回 |
-exec コマンド {} + |
複数のファイルをまとめて1回で渡す | 1回 |
34. 速さを実測(5,000ファイル)
空のファイルを5,000個作り、cat を実行しました。
$ time (find . -name '*.txt' -exec cat {} \;)
real 0m6.463s
$ time (find . -name '*.txt' -exec cat {} +)
real 0m0.042s
$ time (find . -name '*.txt' -print0 | xargs -0 cat)
real 0m0.040s
$ find . -name '*.txt' -exec sh -c 'echo $#' sh {} +
5000
この環境では + が \; の約150倍速くなりました。+ は5,000個を1回のコマンドにまとめて渡していることが $#(引数の数)で確認できます。コマンドが「ファイルを1つしか受け付けない」場合だけ \; を使い、それ以外は + を選ぶのがおすすめです。
xargs と -print0:空白・改行を含むファイル名
35. そのまま xargs に渡すと空白や改行で分かれる
$ find logs -name '*.log' | xargs wc -l
2 logs/app_60h.log
wc: logs/my: No such file or directory
wc: report.log: No such file or directory
2 logs/app_84h.log
2 logs/app_180h.log
wc: logs/line: No such file or directory
wc: break.log: No such file or directory
2 logs/app_12h.log
2 logs/app_400h.log
2 logs/app_36h.log
12 total
36. -print0 と xargs -0 で区切りを NUL 文字にする
$ find logs -name '*.log' -print0 | xargs -0 wc -l
2 logs/app_60h.log
1 logs/my report.log
2 logs/app_84h.log
2 logs/app_180h.log
1 'logs/line'$'\n''break.log'
2 logs/app_12h.log
2 logs/app_400h.log
2 logs/app_36h.log
14 total
NUL 文字(\0)はファイル名に使えない唯一の文字なので、区切りに使うとどんな名前でも正しく渡せます。-exec {} + でも同じ結果になります。
37. for 文で回すときも同じ注意が必要
$ for f in $(find logs -name 'my*'); do echo "[$f]"; done
[logs/my]
[report.log]
$ find logs -name 'my*' -print0 | while IFS= read -r -d '' f; do echo "[$f]"; done
[logs/my report.log]
| 渡し方 | 空白入り | 改行入り |
|---|---|---|
find | xargs |
分かれる | 分かれる |
for f in $(find ...) |
分かれる | 分かれる |
find -print0 | xargs -0 |
○ | ○ |
find -exec ... {} + |
○ | ○ |
-print0 | while read -r -d '' |
○ | ○ |
表示形式を変える・grep と組み合わせる
38. -printf で好きな形式に(GNU のみ)
$ find src -type f -printf '%M %u %s %TY-%Tm-%Td %p\n' | sort -k5
-rw-r--r-- root 2 2026-09-29 src/App.JS
-rw-r--r-- root 28 2026-09-29 src/app.js
-rw-r--r-- root 0 2026-09-29 src/empty.txt
-rwxrwxrwx root 17 2026-09-29 src/util.ts
$ find docs -maxdepth 1 -type f -printf '%f\n' | sort
README.md
guide.md
manual.pdf
one_k.bin
small.bin
| 指定 | 意味 |
|---|---|
%p / %f |
パス / ファイル名だけ |
%s |
サイズ(バイト) |
%M / %u |
権限の表示 / 所有者 |
%TY-%Tm-%Td |
更新日(年-月-日) |
39. 中身に TODO を含むファイルを探す
$ find . -type f \( -name '*.js' -o -name '*.md' \) -not -path './node_modules/*' -exec grep -Hn TODO {} +
./src/app.js:2:// TODO: fix
./docs/README.md:2:TODO: write
名前だけの条件なら、grep の再帰検索でも同じことができます。更新日時やサイズの条件を組み合わせたいときは find と grep の組み合わせが向いています。
$ grep -rn --include='*.js' --include='*.md' --exclude-dir=node_modules TODO . | sort
./docs/README.md:2:TODO: write
./src/app.js:2:// TODO: fix
40. 1件見つけたら終了・シンボリックリンクをたどる・権限エラーを隠す
$ find . -name '*.js' -print -quit
./src/app.js
$ find link_src -name '*.js' # link_src は src へのシンボリックリンク
(出力なし)
$ find -L link_src -name '*.js'
link_src/app.js
$ find locked -name a # 一般ユーザーで実行
find: ‘locked’: Permission denied
$ find locked -name a 2>/dev/null; echo $?
1
2>/dev/null でエラー表示を隠しても、終了ステータスは1(一部を読めなかった)になる点は覚えておくとスクリプトで役立ちます。
GNU find と macOS(BSD)find の違い
macOS の find は BSD 系です。macOS 側は実行環境がないため未実行で、Apple が公開している find のマニュアル(find.1)と FreeBSD の公式マニュアルをもとにまとめています(GNU 側は実行して確認)。
| 項目 | GNU find(Linux) | macOS find |
|---|---|---|
| 探す場所の省略 | 可(. とみなす。実行して確認) |
省略不可(find . ... と書く) |
-printf |
○ | なし(-exec stat -f '%z %N' {} + などで代用) |
| 正規表現の方言 | -regextype posix-extended |
-E オプション(GNU では find -E はエラー) |
-mtime の単位 |
日のみ(-mtime -2h はエラー) |
s m h d w が使える(例:-mtime -2h) |
-size の単位 |
c k M G、単位なしは512バイトブロック |
c k M G T P |
-delete -empty -iname -maxdepth -print0 -quit -newerXY -execdir |
○ | ○ |
| ファイル情報の表示 | stat -c '%s %n' |
stat -f '%z %N' |
$ find logs -mtime -2h
find: invalid argument `-2h' to `-mtime'
どちらでも同じように動かしたいときは、-name -type -mtime(日単位)-size(c/k/M)-print0 -exec {} + の範囲で書くと安心です。Homebrew で GNU findutils を入れると、macOS でも gfind として GNU 版を使えます。
find・locate・fd の違い
locate(plocate 1.1.19)と fd(fd-find 9.0.0)も同じ環境で試しました。
| find | locate | fd | |
|---|---|---|---|
| 探し方 | その場でフォルダをたどる | 事前に作ったデータベースを引く | その場でたどる(並列) |
| 最新の状態 | 常に反映 | updatedb するまで反映されない |
常に反映 |
| 条件の豊富さ | 最も多い(権限・所有者・時刻など) | 名前のみ | 名前・拡張子・サイズ・時刻 |
| 標準で除外されるもの | なし | 設定による | 隠しファイル・.gitignore の対象 |
| 入っている環境 | ほぼすべての Linux / macOS | 追加で入れることが多い | 追加で入れる |
$ touch src/newfile.js
$ plocate -d loc.db newfile.js; echo rc=$?
rc=1
$ updatedb -o loc.db -U "$PWD" && plocate -d loc.db newfile.js
(パス省略)/proj/src/newfile.js
$ fdfind -e js # dist/ を .gitignore に書いたリポジトリで実行
main.js
$ find . -name '*.js' -not -path './.git/*' | sort
./dist/out.js
./main.js
fd は .gitignore に書かれたファイルを標準で結果に含めません(-u で含める)。Ubuntu ではコマンド名が fdfind です。すべてのファイルを条件どおりに確実に探すなら find、名前でさっと探すなら fd や locate、と使い分けると便利です。
よくある質問
Q. -mtime +1 は「1日より前」ではないのですか?
A. 経過時間を24時間で割って小数点以下を切り捨てた日数が「1より大きい」、つまり2日(48時間)以上前です。36時間前のファイルは含まれません。時間で正確に指定したいときは -mmin +1440 のように分単位で書きます。
Q. -exec の最後は \; と + のどちらを書けばよいですか?
A. 多くの場合は + がおすすめです。まとめて渡すので実行回数が減り、5,000ファイルの実測では約150倍速くなりました。コマンドが1つのファイルしか受け取れないときや、{} を引数の途中に置きたいときは \; を使います。
Q. 「Permission denied」がたくさん出て結果が見えません。
A. 2>/dev/null を付けるとエラー表示を隠せます。見る必要のない場所は -prune で除外するか、探す場所を絞ると、速く、見やすくなります。
Q. find で見つけたファイルを安全に削除するには?
A. まず同じ条件で -print だけを実行して対象を確認し、そのあと -print を -delete に置き換えます。-delete は必ず条件の後ろに書きます。
まとめ
- 基本形は
find 場所 条件 動作。パターンは'*.js'のように引用符で囲む -mtime +7は8日以上前。日数は切り捨てで数え、正確に指定したいときは-mmin-size -1Mは空ファイルだけ。単位に切り上げて比べるので、細かくはc(バイト)で- OR は
\( ... -o ... \)で囲み、-deleteは条件の後ろに書く -exec {} +は\;よりずっと速い(5,000ファイルで約150倍)- 空白・改行入りのファイル名は
-print0 | xargs -0か-exec {} +で安全に扱える - macOS の find は
-printfがなく、-mtime -2hのような単位が使えるなど細かな違いがある
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「findコマンドの使い方|目的別逆引き40例と-exec・xargsの違い」
株式会社RJCでは、日々のサーバー作業や調査で使うコマンドの小さなコツも、実際に動かした結果とあわせてチームで共有しています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。