ファイル検索
「あのファイルどこだっけ?」— find と fd があれば必ず見つかる。
81 分で読めます40,339 文字
ファイル検索
「あのファイルどこだっけ?」— find と fd があれば必ず見つかる。
この章で学ぶこと
- find の主要な使い方をマスターする
- fd(モダン代替)を使いこなせる
- locate / mlocate によるデータベース検索を理解する
- which / whereis / type によるコマンド検索を使い分ける
- 実務で頻出するファイル検索パターンを身につける
前提知識
このガイドを読む前に、以下の知識があると理解が深まります:
- 基本的なプログラミングの知識
- 関連する基礎概念の理解
- パーミッションと所有者 の内容を理解していること
1. find — 標準のファイル検索ツール
1.1 基本構文
# 基本構文: find [検索開始パス] [条件式] [アクション]
#
# find は指定されたパスを起点にディレクトリツリーを再帰的に走査し、
# 条件にマッチするファイル/ディレクトリを出力する。
# パスを省略すると . (カレントディレクトリ)が使われる。
# 全ファイル・ディレクトリを表示
find .
# 特定のディレクトリを起点に検索
find /var/log
find /home/user/projects
# 複数の起点ディレクトリを指定
find /etc /usr/local/etc -name "*.conf"1.2 名前による検索(-name / -iname / -path / -regex)
# -name: ファイル名のパターンマッチ(シェルグロブ)
find . -name "*.md" # .md ファイルを再帰検索
find . -name "*.txt" # .txt ファイルを再帰検索
find . -name "Makefile" # Makefile を検索
find . -name "*.log" # ログファイルを検索
find . -name "config.*" # config.xxx を検索
# -iname: 大小文字を無視したパターンマッチ
find . -iname "readme*" # README, readme, Readme 全てマッチ
find . -iname "*.jpg" # .jpg, .JPG, .Jpg 全てマッチ
find . -iname "license*" # LICENSE, license, License 全てマッチ
# -path: パス全体に対するパターンマッチ
find . -path "*/src/*.js" # src ディレクトリ内の .js ファイル
find . -path "*/test/*" # test ディレクトリ以下の全ファイル
find . -path "*/.git/*" -prune -o -name "*.py" -print # .git を除外して .py 検索
# -regex: 正規表現によるパス全体のマッチ
find . -regex ".*\.\(js\|ts\|jsx\|tsx\)" # JS/TS 関連ファイル
find . -regextype posix-extended -regex ".*\.(jpg|jpeg|png|gif)" # 画像ファイル
find . -regex ".*/[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}.*" # 日付パターンを含むパス
# ワイルドカードの注意点
# シェルが先にグロブ展開しないよう、パターンは必ずクォートで囲む
find . -name "*.log" # 正しい
# find . -name *.log # シェルが展開するので危険1.3 タイプによる絞り込み(-type)
# -type で検索対象の種類を指定
find . -type f # 通常ファイルのみ
find . -type d # ディレクトリのみ
find . -type l # シンボリックリンクのみ
find . -type b # ブロックデバイス
find . -type c # キャラクタデバイス
find . -type p # 名前付きパイプ(FIFO)
find . -type s # ソケット
# 組み合わせ例
find . -type f -name "*.conf" # 設定ファイル(ファイルのみ)
find . -type d -name "test*" # test で始まるディレクトリ
find . -type l -name "*.so" # .so のシンボリックリンク
# ディレクトリ一覧を取得(プロジェクト構造の確認に便利)
find . -maxdepth 2 -type d | sort1.4 サイズによる絞り込み(-size)
# -size [+-]N[cwbkMG]
# +N: Nより大きい, -N: Nより小さい, N: ちょうどN
# c: バイト, w: ワード(2B), b: ブロック(512B), k: KB, M: MB, G: GB
find . -size +100M # 100MB以上のファイル
find . -size +1G # 1GB以上のファイル
find . -size -1k # 1KB未満のファイル
find . -size 0 # 0バイトのファイル
find . -empty # 空ファイル/空ディレクトリ
# サイズ範囲で絞り込み
find . -size +10M -size -100M # 10MB〜100MB のファイル
find . -size +1k -size -10k # 1KB〜10KB のファイル
# 実務例: ディスク容量を圧迫している大きなファイルを探す
find / -type f -size +500M 2>/dev/null | head -20
find /var -type f -size +100M -exec ls -lh {} \; 2>/dev/null
# 空ディレクトリを探す(掃除用)
find . -type d -empty
find . -type d -empty -delete # 空ディレクトリを削除1.5 日時による絞り込み(-mtime / -atime / -ctime / -newer)
# -mtime: 内容の最終更新日(modification time)
# -atime: 最終アクセス日(access time)
# -ctime: メタデータの最終変更日(change time = パーミッション変更等)
# 単位は「日」。+N は N日より前、-N は N日以内
find . -mtime -7 # 7日以内に変更されたファイル
find . -mtime +30 # 30日以上前に変更されたファイル
find . -mtime 0 # 今日変更されたファイル(24時間以内)
find . -atime -1 # 1日以内にアクセスされたファイル
find . -ctime -3 # 3日以内にメタデータが変更されたファイル
# -mmin / -amin / -cmin: 分単位での指定
find . -mmin -30 # 30分以内に変更されたファイル
find . -mmin +60 # 60分以上前に変更されたファイル
find . -mmin -5 # 5分以内に変更(デバッグに便利)
# -newer: 基準ファイルより新しいファイルを検索
find . -newer reference.txt # reference.txt より新しいファイル
find . -newer /tmp/timestamp # タイムスタンプファイルより新しい
# 実務例: タイムスタンプファイルを活用した差分ファイル検出
touch -t 202601010000 /tmp/since_newyear # 基準タイムスタンプ作成
find . -newer /tmp/since_newyear -type f # 基準以降に変更されたファイル
# 日付範囲で検索(-newerXY を使う方法 ※GNU find)
# -newermt: modification time を文字列で指定
find . -newermt "2026-01-01" ! -newermt "2026-02-01" -type f
# → 2026年1月に変更されたファイル
# 古いファイルの掃除
find /tmp -type f -mtime +7 -delete # 7日以上前の一時ファイルを削除
find /var/log -name "*.gz" -mtime +90 -delete # 90日以上前の圧縮ログを削除1.6 パーミッション・所有者による絞り込み
# -perm: パーミッションで検索
find . -perm 644 # 正確に644のファイル
find . -perm -644 # 644を含む(644以上)ファイル
find . -perm /111 # 実行権限があるファイル(いずれかのビット)
find . -perm -u+x # ユーザーに実行権限があるファイル
find . -perm /o+w # その他に書き込み権限があるファイル
# セキュリティチェック: 危険なパーミッションのファイルを検出
find / -perm -4000 -type f 2>/dev/null # SUID ビットが設定されたファイル
find / -perm -2000 -type f 2>/dev/null # SGID ビットが設定されたファイル
find / -perm /o+w -type f 2>/dev/null # 全ユーザー書き込み可能ファイル
find /home -perm 777 -type f # 権限が緩すぎるファイル
# -user / -group: 所有者・グループで検索
find . -user root # root が所有するファイル
find . -user nobody # nobody が所有するファイル
find . -group www-data # www-data グループのファイル
find . -nouser # 所有者が存在しないファイル
find . -nogroup # グループが存在しないファイル
# 実務例: 特定ユーザーのファイル一覧
find /home/developer -user developer -type f | wc -l # ファイル数カウント
find /var/www -not -user www-data -type f # www-data 以外のファイル1.7 論理演算子と条件の組み合わせ
# AND(暗黙的 / -a)
find . -name "*.log" -size +10M # 10MB以上のログファイル(暗黙的 AND)
find . -name "*.log" -a -size +10M # 明示的 AND(同じ意味)
# OR(-o)
find . \( -name "*.js" -o -name "*.ts" \) # .js または .ts
find . \( -name "*.jpg" -o -name "*.png" -o -name "*.gif" \) # 画像ファイル
find . -type f \( -name "*.log" -o -name "*.tmp" \) -mtime +30 # 古いログ/tmp
# NOT(! / -not)
find . -type f ! -name "*.md" # .md 以外のファイル
find . -not -name "*.pyc" # .pyc 以外のファイル
find . ! -empty # 空でないファイル
find . -type f ! -user root # root 以外が所有するファイル
# 複雑な条件の組み合わせ
# 括弧 \( \) で優先順位を制御(エスケープ必須)
find . -type f \( -name "*.js" -o -name "*.ts" \) ! -path "*/node_modules/*"
# → node_modules を除外した JS/TS ファイル
find . -type f \( -name "*.py" -o -name "*.rb" \) -size +1k -mtime -30
# → 30日以内に変更された 1KB以上の Python/Ruby ファイル
# -prune による除外(効率的なディレクトリスキップ)
find . -path "./.git" -prune -o -type f -print # .git を除外
find . -name "node_modules" -prune -o -name "*.js" -print # node_modules除外
find . \( -name ".git" -o -name "node_modules" -o -name "__pycache__" \) -prune -o -type f -print
# → .git, node_modules, __pycache__ を全て除外1.8 アクション(-exec / -execdir / -ok / -delete / -print)
# -print: デフォルトのアクション(パスを表示)
find . -name "*.md" -print # 明示的に -print(省略可)
# -print0: NULL文字区切りで出力(xargs -0 と組み合わせ)
find . -name "*.txt" -print0 | xargs -0 wc -l # スペース入りファイル名対応
# -printf: カスタムフォーマット出力(GNU find)
find . -type f -printf "%s %p\n" # サイズとパス
find . -type f -printf "%T+ %p\n" | sort # 更新日時でソート
find . -type f -printf "%u %g %m %p\n" # 所有者 グループ パーミッション パス
# -delete: マッチしたファイルを削除(注意して使用!)
find . -name "*.tmp" -delete # .tmp ファイルを削除
find . -type d -empty -delete # 空ディレクトリを削除
find /tmp -mtime +7 -delete # 7日以上前の一時ファイルを削除
# -exec: 各ファイルに対してコマンドを実行
find . -name "*.sh" -exec chmod +x {} \; # シェルスクリプトに実行権限付与
find . -name "*.log" -exec gzip {} \; # ログファイルを圧縮
find . -name "*.bak" -exec rm {} \; # バックアップファイルを削除
find . -name "*.py" -exec grep -l "import os" {} \; # osをimportしているPyファイル
# -exec の末尾
# \; 各ファイルごとに1回コマンドを実行(遅い)
# + まとめてコマンドに渡す(高速、xargs と同等)
find . -name "*.txt" -exec wc -l {} + # まとめて wc に渡す(高速)
find . -name "*.js" -exec grep -l "console.log" {} + # まとめて grep
# -execdir: ファイルが存在するディレクトリで実行(セキュリティ向上)
find . -name "*.sh" -execdir chmod +x {} \;
# -ok: 実行前に確認を求める(対話的)
find . -name "*.tmp" -ok rm {} \; # 1つずつ確認して削除
# xargs との組み合わせ(-exec + と同等だがより柔軟)
find . -name "*.py" | xargs grep "TODO" # スペースなしファイル名向け
find . -name "*.py" -print0 | xargs -0 grep "TODO" # スペース対応版
find . -name "*.css" -print0 | xargs -0 -I{} cp {} /backup/ # 個別コピー
find . -name "*.log" -print0 | xargs -0 -P 4 gzip # 4並列で圧縮1.9 深さ制限と検索順序
# -maxdepth: 検索の最大深度を制限
find . -maxdepth 1 -type f # カレントディレクトリのファイルのみ(再帰しない)
find . -maxdepth 2 -type d # 2階層まで のディレクトリ
find . -maxdepth 3 -name "*.md" # 3階層までの .md ファイル
# -mindepth: 検索の最小深度を指定
find . -mindepth 2 -name "*.py" # 2階層以降の .py ファイル(直下を除外)
find . -mindepth 1 -maxdepth 1 -type d # 直下のディレクトリのみ(ls -d */ と同等)
# -depth: 深さ優先(ディレクトリの内容を先に処理)
find . -depth -name "*.tmp" -delete # 深さ優先で削除(空ディレクトリ対策)
# -mount / -xdev: ファイルシステムをまたがない
find / -mount -name "*.conf" -type f # ルートファイルシステムのみ検索1.10 find の実務パターン集
# --- プロジェクト管理 ---
# プロジェクト内の全ソースファイルを列挙
find ./src -type f \( -name "*.js" -o -name "*.ts" -o -name "*.jsx" -o -name "*.tsx" \) \
! -path "*/node_modules/*" ! -path "*/.next/*" | sort
# プロジェクトのファイル数をディレクトリごとに集計
find . -type f ! -path "*/.git/*" | sed 's|/[^/]*$||' | sort | uniq -c | sort -rn | head -20
# 最近変更されたファイルトップ20
find . -type f ! -path "*/.git/*" -printf "%T@ %T+ %p\n" | sort -rn | head -20
# 重複ファイルの検出(MD5ハッシュベース)
find . -type f -exec md5sum {} + | sort | uniq -w 32 -d
# --- ディスク管理 ---
# サイズ順に大きいファイルを表示
find . -type f -exec ls -lS {} + | head -20
# ディスク使用量トップ10ディレクトリ
find . -maxdepth 3 -type d -exec du -sh {} + 2>/dev/null | sort -rh | head -10
# 古い大きなファイルの検出(アーカイブ候補)
find /data -type f -size +100M -mtime +365 -ls
# --- ログ管理 ---
# 本日のログファイルのみ表示
find /var/log -type f -mtime 0 -name "*.log"
# ログローテーション: 古い圧縮ログの削除
find /var/log -name "*.gz" -mtime +180 -delete
# エラーを含むログファイルを検索
find /var/log -name "*.log" -mtime -1 -exec grep -l "ERROR" {} +
# --- 開発環境 ---
# 全テストファイルを列挙
find . -type f \( -name "*_test.go" -o -name "*_test.py" -o -name "*.test.js" -o -name "*.spec.ts" \)
# __pycache__ の一括削除
find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null
# .DS_Store の一括削除
find . -name ".DS_Store" -delete
# node_modules のサイズ確認
find . -type d -name "node_modules" -exec du -sh {} + 2>/dev/null
# Go の依存パッケージ一覧
find $GOPATH/pkg -type d -maxdepth 4
# --- セキュリティ ---
# ワールドライタブルなファイルを検出
find /var/www -type f -perm /o+w -ls
# SUID/SGID ファイルの監査
find / -type f \( -perm -4000 -o -perm -2000 \) -exec ls -la {} \; 2>/dev/null
# 所有者不明ファイルの検出
find / -nouser -o -nogroup 2>/dev/null
# 最近変更された設定ファイル(改ざん検知)
find /etc -type f -mmin -60 -ls
# --- バックアップ ---
# rsync 用のファイルリスト生成
find /data -type f -newer /tmp/last_backup -print0 > /tmp/backup_list.txt
# tar アーカイブの作成
find ./project -type f -name "*.py" -print0 | tar czf python_files.tar.gz --null -T -
# 差分バックアップ
find /home -type f -mtime -1 -print0 | cpio -0 -pdm /backup/daily/1.11 find のパフォーマンス最適化
# 1. -type を早い段階で指定(ディレクトリエントリのタイプチェックはコスト低)
find . -type f -name "*.log" # 良い: type を先に
# find . -name "*.log" -type f # 動作は同じだが、type 先の方が一般的
# 2. -prune で不要ディレクトリをスキップ
find . -path "./.git" -prune -o -type f -name "*.py" -print
# → .git 以下を完全にスキップするため高速
# 3. -exec + を使う(\; より高速)
find . -name "*.txt" -exec wc -l {} + # 良い: まとめて実行
# find . -name "*.txt" -exec wc -l {} \; # 遅い: 1ファイルずつ実行
# 4. -maxdepth で検索範囲を限定
find . -maxdepth 3 -name "*.conf" # 3階層までに制限
# 5. 標準エラーを抑制(権限エラーの無視)
find / -name "*.conf" 2>/dev/null
find / -name "*.conf" 2>&1 | grep -v "Permission denied"
# 6. 並列処理(xargs -P)
find . -name "*.png" -print0 | xargs -0 -P $(nproc) optipng -o7
# → CPU コア数分の並列で画像最適化
# 7. find の結果をファイルに保存して再利用
find /data -type f -name "*.csv" > /tmp/csv_files.txt
while IFS= read -r file; do
process_csv "$file"
done < /tmp/csv_files.txt2. fd — モダンな代替ツール
2.1 インストールと概要
# インストール
brew install fd # macOS
sudo apt install fd-find # Ubuntu/Debian(コマンド名は fdfind)
sudo pacman -S fd # Arch Linux
cargo install fd-find # Rust (Cargo)
# Ubuntu/Debian では fdfind という名前になるため、エイリアスを設定
alias fd='fdfind'
# または ~/.bashrc に追加
# fd の特徴
# - .gitignore を自動で尊重(--no-ignore で無効化可能)
# - カラー出力がデフォルト
# - 正規表現がデフォルト(-g でグロブに切替)
# - Unicode 対応
# - find より簡潔な構文
# - 並列実行による高速化2.2 基本的な使い方
# 基本構文: fd [パターン] [検索パス]
# パターンなし: 全ファイルを表示
fd # 全ファイル(.gitignore を尊重)
# 文字列パターン(部分一致、正規表現)
fd readme # "readme" を含むファイル/ディレクトリ
fd "\.md$" # 正規表現: .md で終わるファイル
fd "^test" # test で始まるファイル
fd "[0-9]{4}" # 4桁の数字を含むファイル
fd "config\.(json|yaml|toml)" # 設定ファイル(複数拡張子)
# -g: グロブパターン(find の -name に近い)
fd -g "*.md" # グロブで .md ファイル検索
fd -g "Makefile" # 完全一致
fd -g "*.{js,ts,jsx,tsx}" # 複数拡張子
# 検索パスの指定
fd "\.py$" /home/user/projects # 特定ディレクトリ以下を検索
fd "\.rs$" src/ # src/ 以下の Rust ファイル2.3 主要オプション
# 拡張子で検索(-e / --extension)
fd -e md # .md ファイル
fd -e py # .py ファイル
fd -e jpg -e png -e gif # 画像ファイル(複数拡張子)
fd -e rs -e toml # Rust プロジェクト関連
# タイプで絞り込み(-t / --type)
fd -t f # ファイルのみ (file)
fd -t d # ディレクトリのみ (directory)
fd -t l # シンボリックリンクのみ (symlink)
fd -t x # 実行可能ファイルのみ (executable)
fd -t e # 空ファイル/ディレクトリ (empty)
# 隠しファイル(-H / --hidden)
fd -H # 隠しファイル含む(.gitignore は引き続き尊重)
fd -H "\.env" # .env ファイル検索
# .gitignore を無視(-I / --no-ignore)
fd -I # .gitignore を無視
fd -HI # 隠しファイル + .gitignore 無視(find と同等)
# 大小文字の制御
fd -s "README" # 大小文字を区別(-s / --case-sensitive)
fd -i "readme" # 大小文字を無視(-i / --ignore-case)
# デフォルト: パターンが全て小文字なら case-insensitive(スマートケース)
# 深さ制限
fd -d 1 # 1階層のみ(--max-depth)
fd -d 3 # 3階層まで
fd --min-depth 2 # 2階層以降
# 除外パターン(-E / --exclude)
fd -E node_modules # node_modules を除外
fd -E "*.min.js" # minified JS を除外
fd -E ".git" -E "target" # 複数ディレクトリを除外
# サイズフィルタ(-S / --size)
fd -S +1m # 1MB 以上
fd -S -10k # 10KB 以下
fd -S +100k -S -1m # 100KB〜1MB
# 日時フィルタ
fd --changed-within 1h # 1時間以内に変更
fd --changed-within 2d # 2日以内に変更
fd --changed-before 1w # 1週間以上前に変更
fd --changed-within "2026-01-01" # 指定日以降に変更
# 所有者フィルタ
fd --owner root # root が所有するファイル
fd --owner ":www-data" # www-data グループのファイル2.4 アクション実行(-x / -X)
# -x / --exec: 各ファイルに対してコマンドを実行
fd -e txt -x wc -l # 各 .txt ファイルの行数
fd -e sh -x chmod +x # シェルスクリプトに実行権限
fd -e bak -x rm # .bak ファイルを削除
fd -e png -x optipng # PNG 最適化
# プレースホルダ
# {} フルパス
# {/} ファイル名のみ(ディレクトリ部分なし)
# {//} ディレクトリ部分のみ
# {.} 拡張子を除いたパス
# {/.} 拡張子を除いたファイル名
fd -e jpg -x convert {} {.}.png # JPG → PNG 変換
fd -e md -x echo "File: {/}, Dir: {//}" # ファイル名とディレクトリ
# -X / --exec-batch: 全結果をまとめて1回のコマンドに渡す(find -exec + 相当)
fd -e py -X wc -l # 全 .py ファイルの行数を一括カウント
fd -e js -X eslint # 全 JS ファイルを一括 lint
# 並列実行(-j / --threads)
fd -e png -x optipng -j 4 # 4スレッドで並列処理
fd -e mp4 -x ffmpeg -i {} {.}.webm -j 2 # 2並列で動画変換2.5 fd の実務パターン集
# プロジェクト内の全ソースファイル行数を集計
fd -e py -X wc -l | tail -1
# 特定パターンのファイルを一括リネーム
fd -e txt -x mv {} {.}.md # .txt → .md
# Docker 関連ファイルを検索
fd -g "Dockerfile*" -g "docker-compose*" -g ".dockerignore"
# 最近変更されたファイルの一覧(更新日時付き)
fd -t f --changed-within 1d -x ls -la
# テストファイルを除外してソースファイルを列挙
fd -e py -E "*_test.py" -E "test_*" -E "conftest.py"
# 設定ファイルの一括検索
fd -e yaml -e yml -e json -e toml -e ini -e conf
# Git で追跡されていないファイルの中から検索
fd -I -t f "\.log$" # .gitignore を無視してログファイル検索2.6 find と fd の比較表
| 機能 | find | fd |
|---|---|---|
| デフォルト動作 | 全ファイルを表示 | .gitignore 尊重 |
| パターン | シェルグロブ(-name) | 正規表現(デフォルト) |
| 大小文字 | 区別(-iname で無視) | スマートケース |
| 出力 | モノクロ | カラー表示 |
| 速度 | 標準的 | 高速(並列処理) |
| 構文 | 冗長(-name, -type等) | 簡潔 |
| 環境 | 標準搭載 | 別途インストール |
| スクリプト | POSIX 互換 | 非標準 |
| 隠しファイル | 含む | デフォルト除外 |
| -exec 相当 | -exec {} \; / + | -x / -X |
| 深さ制限 | -maxdepth / -mindepth | -d / --min-depth |
| サイズ | -size +10M | -S +10m |
使い分けガイド:
- 日常的な検索 → fd(シンプルで高速)
- シェルスクリプト/CI → find(POSIX 互換、標準搭載)
- 複雑な条件 → find(論理演算子が豊富)
- .gitignore 尊重 → fd(デフォルト対応)
3. locate / mlocate / plocate — データベースベースの高速検索
3.1 概要とインストール
# locate はファイルシステムのデータベースを使った高速検索
# find/fd がリアルタイムにディスクを走査するのに対し、
# locate は事前に構築されたデータベースを検索するため非常に高速
# インストール
sudo apt install mlocate # Ubuntu/Debian(mlocate)
sudo apt install plocate # Ubuntu 22.04+(plocate、より高速)
sudo yum install mlocate # CentOS/RHEL
brew install findutils # macOS(glocate として利用可能)
# データベースの初期構築
sudo updatedb # データベースの構築/更新
# cron で毎日自動更新される(通常 /etc/cron.daily/mlocate)3.2 基本的な使い方
# 基本: locate [パターン]
locate filename # パス名でデータベース検索
locate "*.conf" # ワイルドカード
locate -i "readme" # 大小文字無視
locate -n 10 "*.log" # 結果を10件に制限
locate -c "*.py" # マッチ数をカウント
locate -b "filename" # ベースネーム(ファイル名部分)のみで検索
locate -r "\.py$" # 正規表現で検索
# データベースの確認
locate -S # データベースの統計情報表示
# → ファイル数、ディレクトリ数、データベースサイズ等
# データベースの手動更新
sudo updatedb # 全体更新
# updatedb の設定: /etc/updatedb.conf
# PRUNEPATHS: 除外するパス(/tmp, /proc 等)
# PRUNEFS: 除外するファイルシステム(nfs, tmpfs 等)3.3 locate の注意点と使い分け
# locate の制限事項:
# 1. データベースが古い場合、最近のファイルが見つからない
# → sudo updatedb で手動更新するか、find/fd を使う
# 2. 権限を考慮しない場合がある(設定による)
# 3. 削除済みファイルが表示される可能性がある
# 使い分けガイド:
# - ファイル名だけで素早く探したい → locate
# - 最新の状態を確実に検索したい → find / fd
# - サイズ・日時等の条件が必要 → find / fd
# - サーバー上の設定ファイル検索 → locate(高速で便利)
# 実務例: locate + grep の組み合わせ
locate "nginx.conf" | grep -v backup # バックアップを除外
locate -b "settings.py" | head -5 # Django の設定ファイルを素早く発見4. which / whereis / type — コマンドの場所を検索
4.1 which
# which: 実行可能ファイルのパスを表示(PATH から検索)
which python # /usr/bin/python
which -a python # PATH 上の全 python を表示
which node # /usr/local/bin/node
which gcc # コンパイラのパス確認
# 実務例
which python3 && python3 --version # Python3 があればバージョン表示
if which docker > /dev/null 2>&1; then
echo "Docker is installed"
fi4.2 whereis
# whereis: バイナリ、ソース、マニュアルの場所を検索
whereis python # バイナリ、マニュアル等
whereis -b python # バイナリのみ
whereis -m python # マニュアルのみ
whereis -s python # ソースのみ
whereis ls grep awk # 複数コマンドを一括検索4.3 type
# type: コマンドの種類を表示(bash 組み込み)
type ls # ls はエイリアス / 関数 / 外部コマンド のいずれか
type cd # cd is a shell builtin
type ll # ll is aliased to `ls -la`
type -a python # 全ての候補を表示
type -t ls # 種類だけ表示(alias, builtin, function, file)
# 実務例: コマンドの実体確認
type -a grep # grep がエイリアスか外部コマンドか確認
type -a python python3 pip pip3 # Python 環境の確認5. 高度なファイル検索テクニック
5.1 fzf を活用したインタラクティブ検索
# fzf: ファジーファインダー(対話的に絞り込み検索)
# brew install fzf
# 基本的な使い方
find . -type f | fzf # 全ファイルからインタラクティブ選択
fd -t f | fzf # fd + fzf の組み合わせ
# fzf + エディタ
vim $(fzf) # fzf で選択したファイルを vim で開く
code $(fd -e py | fzf -m) # 複数選択して VS Code で開く
# fzf + preview
fd -t f | fzf --preview 'bat --color=always {}' # プレビュー付き
fd -t f | fzf --preview 'head -50 {}' # head でプレビュー
# fzf + kill(プロセス検索 & kill)
ps aux | fzf | awk '{print $2}' | xargs kill
# fzf + git
git log --oneline | fzf --preview 'git show {1}' # コミット選択
git branch | fzf | xargs git checkout # ブランチ切替
# .bashrc / .zshrc に設定するキーバインド
export FZF_DEFAULT_COMMAND='fd -t f --hidden --exclude .git'
export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
# Ctrl+T: ファイル検索、Ctrl+R: コマンド履歴検索、Alt+C: ディレクトリ移動5.2 tree コマンドによるディレクトリ構造の可視化
# tree: ディレクトリ構造をツリー形式で表示
# brew install tree
tree # カレントディレクトリのツリー表示
tree -L 2 # 2階層まで表示
tree -d # ディレクトリのみ
tree -a # 隠しファイル含む
tree -I "node_modules|.git" # 特定ディレクトリを除外
tree -P "*.py" # Python ファイルのみ
tree --prune # 空ディレクトリを非表示
tree -s # ファイルサイズ表示
tree -D # 更新日時表示
tree -h # 人間が読みやすいサイズ表示
tree -f # フルパス表示
tree --du # ディレクトリの合計サイズ
# 実務例: プロジェクト構造の文書化
tree -L 3 -I "node_modules|.git|__pycache__|.next" > project_structure.txt5.3 ファイル検索の自動化スクリプト
#!/bin/bash
# find_large_files.sh - 指定サイズ以上のファイルを検索してレポート
set -euo pipefail
SEARCH_DIR="${1:-.}"
MIN_SIZE="${2:-100M}"
OUTPUT_FILE="/tmp/large_files_report_$(date +%Y%m%d_%H%M%S).txt"
echo "=== 大容量ファイルレポート ===" > "$OUTPUT_FILE"
echo "検索ディレクトリ: $SEARCH_DIR" >> "$OUTPUT_FILE"
echo "最小サイズ: $MIN_SIZE" >> "$OUTPUT_FILE"
echo "実行日時: $(date)" >> "$OUTPUT_FILE"
echo "---" >> "$OUTPUT_FILE"
find "$SEARCH_DIR" -type f -size +"$MIN_SIZE" -printf "%s\t%p\n" 2>/dev/null \
| sort -rn \
| while IFS=$'\t' read -r size path; do
hr_size=$(numfmt --to=iec "$size" 2>/dev/null || echo "${size}B")
echo "$hr_size $path"
done >> "$OUTPUT_FILE"
total=$(grep -c "^" "$OUTPUT_FILE" 2>/dev/null || echo "0")
echo "---" >> "$OUTPUT_FILE"
echo "合計: $((total - 5)) ファイル" >> "$OUTPUT_FILE"
echo "レポート出力先: $OUTPUT_FILE"
cat "$OUTPUT_FILE"#!/bin/bash
# find_duplicates.sh - 重複ファイルの検出
set -euo pipefail
SEARCH_DIR="${1:-.}"
echo "=== 重複ファイル検出 ==="
echo "検索ディレクトリ: $SEARCH_DIR"
echo ""
# 同じサイズのファイルをグループ化し、MD5で比較
find "$SEARCH_DIR" -type f ! -empty -printf "%s %p\n" 2>/dev/null \
| sort -n \
| uniq -w 10 -D \
| awk '{print $2}' \
| xargs -d '\n' md5sum 2>/dev/null \
| sort \
| uniq -w 32 -D \
| awk '{
if (prev_hash == substr($0, 1, 32)) {
print " DUP: " $2
} else {
if (NR > 1) print ""
print "ORIG: " $2
}
prev_hash = substr($0, 1, 32)
}'#!/bin/bash
# cleanup_project.sh - プロジェクトの不要ファイル掃除
set -euo pipefail
PROJECT_DIR="${1:-.}"
DRY_RUN="${2:-true}" # デフォルトはドライラン
echo "=== プロジェクトクリーンアップ ==="
echo "対象: $PROJECT_DIR"
echo "ドライラン: $DRY_RUN"
echo ""
# 削除対象パターン
PATTERNS=(
"*.pyc"
"__pycache__"
".DS_Store"
"Thumbs.db"
"*.swp"
"*.swo"
"*~"
"*.bak"
"*.orig"
)
total_size=0
total_count=0
for pattern in "${PATTERNS[@]}"; do
files=$(find "$PROJECT_DIR" -name "$pattern" -not -path "*/.git/*" 2>/dev/null)
if [ -n "$files" ]; then
count=$(echo "$files" | wc -l)
size=$(echo "$files" | xargs -I{} stat -f%z {} 2>/dev/null | awk '{s+=$1}END{print s+0}')
total_count=$((total_count + count))
total_size=$((total_size + size))
echo "[$pattern] $count ファイル ($((size / 1024)) KB)"
if [ "$DRY_RUN" = "false" ]; then
echo "$files" | xargs rm -rf
echo " → 削除完了"
fi
fi
done
echo ""
echo "合計: $total_count ファイル ($((total_size / 1024)) KB)"
if [ "$DRY_RUN" = "true" ]; then
echo "※ ドライランモードです。実際に削除するには第2引数に false を指定してください。"
fi6. トラブルシューティング
6.1 find でよくあるエラーと対処法
# エラー: "Permission denied"
# 対処: 標準エラーを抑制
find / -name "*.conf" 2>/dev/null
find / -name "*.conf" 2>&1 | grep -v "Permission denied"
# エラー: "Argument list too long"(ファイルが多すぎる場合)
# 対処: xargs を使う
find . -name "*.log" -print0 | xargs -0 rm # OK
# find . -name "*.log" -exec rm {} + # これも OK
# rm $(find . -name "*.log") # NG: 引数が多すぎる
# エラー: ファイル名にスペースや特殊文字が含まれる
# 対処: -print0 と xargs -0 を使う
find . -name "*.txt" -print0 | xargs -0 grep "keyword"
# エラー: -prune が期待通り動作しない
# 対処: -prune は -o (OR) と組み合わせる
find . -name ".git" -prune -o -type f -print # 正しい
# find . -name ".git" -prune -type f -print # 誤り
# エラー: macOS の find で GNU find のオプションが使えない
# 対処: GNU find をインストール
# brew install findutils
# gfind を使うか、PATH に追加
# macOS find は BSD find であり、-printf 等が使えない
gfind . -type f -printf "%T+ %p\n" # GNU find を使用
# -delete が最初の条件として使われた場合の警告
# 対処: 必ず条件を先に指定
find . -name "*.tmp" -delete # 正しい
# find . -delete -name "*.tmp" # 危険!全ファイル削除の恐れ6.2 検索のデバッグ
# find の動作を確認するためのテクニック
# 1. まず -print で結果を確認してから -delete / -exec
find . -name "*.tmp" -print # まず確認
find . -name "*.tmp" -delete # 確認後に削除
# 2. -ok で対話的に確認
find . -name "*.bak" -ok rm {} \; # 1つずつ確認
# 3. echo で実行されるコマンドを確認
find . -name "*.sh" -exec echo chmod +x {} \; # 実際には実行しない
# 4. 件数を確認
find . -name "*.log" | wc -l # マッチ件数を確認
# 5. fd の --list-file-types でサポートされるファイルタイプ確認
fd --list-file-types # fd が認識するファイルタイプ一覧実践演習
演習1: 基本的な実装
以下の要件を満たすコードを実装してください。
要件:
- 入力データの検証を行うこと
- エラーハンドリングを適切に実装すること
- テストコードも作成すること
# 演習1: 基本実装のテンプレート
class Exercise1:
"""基本的な実装パターンの演習"""
def __init__(self):
self.data = []
def validate_input(self, value):
"""入力値の検証"""
if value is None:
raise ValueError("入力値がNoneです")
return True
def process(self, value):
"""データ処理のメインロジック"""
self.validate_input(value)
self.data.append(value)
return self.data
def get_results(self):
"""処理結果の取得"""
return {
'count': len(self.data),
'data': self.data
}
# テスト
def test_exercise1():
ex = Exercise1()
assert ex.process(1) == [1]
assert ex.process(2) == [1, 2]
assert ex.get_results()['count'] == 2
try:
ex.process(None)
assert False, "例外が発生するべき"
except ValueError:
pass
print("全テスト合格!")
test_exercise1()演習2: 応用パターン
基本実装を拡張して、以下の機能を追加してください。
# 演習2: 応用パターン
from typing import List, Dict, Optional
from datetime import datetime
class AdvancedExercise:
"""応用パターンの演習"""
def __init__(self, max_size: int = 100):
self._items: List[Dict] = []
self._max_size = max_size
self._created_at = datetime.now()
def add(self, key: str, value: any) -> bool:
"""アイテムの追加(サイズ制限付き)"""
if len(self._items) >= self._max_size:
return False
self._items.append({
'key': key,
'value': value,
'timestamp': datetime.now().isoformat()
})
return True
def find(self, key: str) -> Optional[Dict]:
"""キーによる検索"""
for item in reversed(self._items):
if item['key'] == key:
return item
return None
def remove(self, key: str) -> bool:
"""キーによる削除"""
for i, item in enumerate(self._items):
if item['key'] == key:
self._items.pop(i)
return True
return False
def stats(self) -> Dict:
"""統計情報"""
return {
'total_items': len(self._items),
'max_size': self._max_size,
'usage_percent': len(self._items) / self._max_size * 100,
'uptime': str(datetime.now() - self._created_at)
}
# テスト
def test_advanced():
ex = AdvancedExercise(max_size=3)
assert ex.add("a", 1) == True
assert ex.add("b", 2) == True
assert ex.add("c", 3) == True
assert ex.add("d", 4) == False # サイズ制限
assert ex.find("b")['value'] == 2
assert ex.remove("b") == True
assert ex.find("b") is None
stats = ex.stats()
assert stats['total_items'] == 2
print("応用テスト全合格!")
test_advanced()演習3: パフォーマンス最適化
以下のコードのパフォーマンスを改善してください。
# 演習3: パフォーマンス最適化
import time
from functools import lru_cache
# 最適化前(O(n^2))
def slow_search(data: list, target: int) -> int:
"""非効率な検索"""
for i in range(len(data)):
for j in range(i + 1, len(data)):
if data[i] + data[j] == target:
return (i, j)
return (-1, -1)
# 最適化後(O(n))
def fast_search(data: list, target: int) -> tuple:
"""ハッシュマップを使った効率的な検索"""
seen = {}
for i, num in enumerate(data):
complement = target - num
if complement in seen:
return (seen[complement], i)
seen[num] = i
return (-1, -1)
# ベンチマーク
def benchmark():
import random
data = list(range(5000))
random.shuffle(data)
target = data[100] + data[4000]
start = time.time()
result1 = slow_search(data, target)
slow_time = time.time() - start
start = time.time()
result2 = fast_search(data, target)
fast_time = time.time() - start
print(f"非効率版: {slow_time:.4f}秒")
print(f"効率版: {fast_time:.6f}秒")
print(f"高速化率: {slow_time/fast_time:.0f}倍")
benchmark()ポイント:
- アルゴリズムの計算量を意識する
- 適切なデータ構造を選択する
- ベンチマークで効果を測定する
設計判断ガイド
選択基準マトリクス
技術選択を行う際の判断基準を以下にまとめます。
| 判断基準 | 重視する場合 | 妥協できる場合 |
|---|---|---|
| パフォーマンス | リアルタイム処理、大規模データ | 管理画面、バッチ処理 |
| 保守性 | 長期運用、チーム開発 | プロトタイプ、短期プロジェクト |
| スケーラビリティ | 成長が見込まれるサービス | 社内ツール、固定ユーザー |
| セキュリティ | 個人情報、金融データ | 公開データ、社内利用 |
| 開発速度 | MVP、市場投入スピード | 品質重視、ミッションクリティカル |
アーキテクチャパターンの選択
| アーキテクチャ選択フロー |
|---|
| ① チーム規模は? |
| ├─ 小規模(1-5人)→ モノリス |
| └─ 大規模(10人+)→ ②へ |
| ② デプロイ頻度は? |
| ├─ 週1回以下 → モノリス + モジュール分割 |
| └─ 毎日/複数回 → ③へ |
| ③ チーム間の独立性は? |
| ├─ 高い → マイクロサービス |
| └─ 中程度 → モジュラーモノリス |
トレードオフの分析
技術的な判断には必ずトレードオフが伴います。以下の観点で分析を行いましょう:
1. 短期 vs 長期のコスト
- 短期的に速い方法が長期的には技術的負債になることがある
- 逆に、過剰な設計は短期的なコストが高く、プロジェクトの遅延を招く
2. 一貫性 vs 柔軟性
- 統一された技術スタックは学習コストが低い
- 多様な技術の採用は適材適所が可能だが、運用コストが増加
3. 抽象化のレベル
- 高い抽象化は再利用性が高いが、デバッグが困難になる場合がある
- 低い抽象化は直感的だが、コードの重複が発生しやすい
# 設計判断の記録テンプレート
class ArchitectureDecisionRecord:
"""ADR (Architecture Decision Record) の作成"""
def __init__(self, title: str):
self.title = title
self.context = ""
self.decision = ""
self.consequences = []
self.alternatives = []
def set_context(self, context: str):
"""背景と課題の記述"""
self.context = context
return self
def set_decision(self, decision: str):
"""決定内容の記述"""
self.decision = decision
return self
def add_consequence(self, consequence: str, positive: bool = True):
"""結果の追加"""
self.consequences.append({
'description': consequence,
'type': 'positive' if positive else 'negative'
})
return self
def add_alternative(self, name: str, reason_rejected: str):
"""却下した代替案の追加"""
self.alternatives.append({
'name': name,
'reason_rejected': reason_rejected
})
return self
def to_markdown(self) -> str:
"""Markdown形式で出力"""
md = f"# ADR: {self.title}\n\n"
md += f"## 背景\n{self.context}\n\n"
md += f"## 決定\n{self.decision}\n\n"
md += "## 結果\n"
for c in self.consequences:
icon = "✅" if c['type'] == 'positive' else "⚠️"
md += f"- {icon} {c['description']}\n"
md += "\n## 却下した代替案\n"
for a in self.alternatives:
md += f"- **{a['name']}**: {a['reason_rejected']}\n"
return md実務での適用シナリオ
シナリオ1: スタートアップでのMVP開発
状況: 限られたリソースで素早くプロダクトをリリースする必要がある
アプローチ:
- シンプルなアーキテクチャを選択
- 必要最小限の機能に集中
- 自動テストはクリティカルパスのみ
- モニタリングは早期から導入
学んだ教訓:
- 完璧を求めすぎない(YAGNI原則)
- ユーザーフィードバックを早期に取得
- 技術的負債は意識的に管理する
シナリオ2: レガシーシステムのモダナイゼーション
状況: 10年以上運用されているシステムを段階的に刷新する
アプローチ:
- Strangler Fig パターンで段階的に移行
- 既存のテストがない場合はCharacterization Testを先に作成
- APIゲートウェイで新旧システムを共存
- データ移行は段階的に実施
| フェーズ | 作業内容 | 期間目安 | リスク |
|---|---|---|---|
| 1. 調査 | 現状分析、依存関係の把握 | 2-4週間 | 低 |
| 2. 基盤 | CI/CD構築、テスト環境 | 4-6週間 | 低 |
| 3. 移行開始 | 周辺機能から順次移行 | 3-6ヶ月 | 中 |
| 4. コア移行 | 中核機能の移行 | 6-12ヶ月 | 高 |
| 5. 完了 | 旧システム廃止 | 2-4週間 | 中 |
シナリオ3: 大規模チームでの開発
状況: 50人以上のエンジニアが同一プロダクトを開発する
アプローチ:
- ドメイン駆動設計で境界を明確化
- チームごとにオーナーシップを設定
- 共通ライブラリはInner Source方式で管理
- APIファーストで設計し、チーム間の依存を最小化
# チーム間のAPI契約定義
from dataclasses import dataclass
from typing import List, Optional
from enum import Enum
class Priority(Enum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
CRITICAL = "critical"
@dataclass
class APIContract:
"""チーム間のAPI契約"""
endpoint: str
method: str
owner_team: str
consumers: List[str]
sla_ms: int # レスポンスタイムSLA
priority: Priority
def validate_sla(self, actual_ms: int) -> bool:
"""SLA準拠の確認"""
return actual_ms <= self.sla_ms
def to_openapi(self) -> dict:
"""OpenAPI形式で出力"""
return {
'path': self.endpoint,
'method': self.method,
'x-owner': self.owner_team,
'x-consumers': self.consumers,
'x-sla-ms': self.sla_ms
}
# 使用例
contracts = [
APIContract(
endpoint="/api/v1/users",
method="GET",
owner_team="user-team",
consumers=["order-team", "notification-team"],
sla_ms=200,
priority=Priority.HIGH
),
APIContract(
endpoint="/api/v1/orders",
method="POST",
owner_team="order-team",
consumers=["payment-team", "inventory-team"],
sla_ms=500,
priority=Priority.CRITICAL
)
]シナリオ4: パフォーマンスクリティカルなシステム
状況: ミリ秒単位のレスポンスが求められるシステム
最適化ポイント:
- キャッシュ戦略(L1: インメモリ、L2: Redis、L3: CDN)
- 非同期処理の活用
- コネクションプーリング
- クエリ最適化とインデックス設計
| 最適化手法 | 効果 | 実装コスト | 適用場面 |
|---|---|---|---|
| インメモリキャッシュ | 高 | 低 | 頻繁にアクセスされるデータ |
| CDN | 高 | 低 | 静的コンテンツ |
| 非同期処理 | 中 | 中 | I/O待ちが多い処理 |
| DB最適化 | 高 | 高 | クエリが遅い場合 |
| コード最適化 | 低-中 | 高 | CPU律速の場合 |
チーム開発での活用
コードレビューのチェックリスト
このトピックに関連するコードレビューで確認すべきポイント:
- 命名規則が一貫しているか
- エラーハンドリングが適切か
- テストカバレッジは十分か
- パフォーマンスへの影響はないか
- セキュリティ上の問題はないか
- ドキュメントは更新されているか
ナレッジ共有のベストプラクティス
| 方法 | 頻度 | 対象 | 効果 |
|---|---|---|---|
| ペアプログラミング | 随時 | 複雑なタスク | 即時のフィードバック |
| テックトーク | 週1回 | チーム全体 | 知識の水平展開 |
| ADR (設計記録) | 都度 | 将来のメンバー | 意思決定の透明性 |
| 振り返り | 2週間ごと | チーム全体 | 継続的改善 |
| モブプログラミング | 月1回 | 重要な設計 | 合意形成 |
技術的負債の管理
優先度マトリクス:
影響度 高
│| 計画 | 即座 |
|---|---|
| 的に | に |
| 対応 | 対応 |
| 記録 | 次の |
| のみ | Sprint |
| で |
│
影響度 低
発生頻度 低 発生頻度 高
セキュリティの考慮事項
一般的な脆弱性と対策
| 脆弱性 | リスクレベル | 対策 | 検出方法 |
|---|---|---|---|
| インジェクション攻撃 | 高 | 入力値のバリデーション・パラメータ化クエリ | SAST/DAST |
| 認証の不備 | 高 | 多要素認証・セッション管理の強化 | ペネトレーションテスト |
| 機密データの露出 | 高 | 暗号化・アクセス制御 | セキュリティ監査 |
| 設定の不備 | 中 | セキュリティヘッダー・最小権限の原則 | 構成スキャン |
| ログの不足 | 中 | 構造化ログ・監査証跡 | ログ分析 |
セキュアコーディングのベストプラクティス
# セキュアコーディング例
import hashlib
import secrets
import hmac
from typing import Optional
class SecurityUtils:
"""セキュリティユーティリティ"""
@staticmethod
def generate_token(length: int = 32) -> str:
"""暗号学的に安全なトークン生成"""
return secrets.token_urlsafe(length)
@staticmethod
def hash_password(password: str, salt: Optional[str] = None) -> tuple:
"""パスワードのハッシュ化"""
if salt is None:
salt = secrets.token_hex(16)
hashed = hashlib.pbkdf2_hmac(
'sha256',
password.encode('utf-8'),
salt.encode('utf-8'),
iterations=100000
)
return hashed.hex(), salt
@staticmethod
def verify_password(password: str, hashed: str, salt: str) -> bool:
"""パスワードの検証"""
new_hash, _ = SecurityUtils.hash_password(password, salt)
return hmac.compare_digest(new_hash, hashed)
@staticmethod
def sanitize_input(value: str) -> str:
"""入力値のサニタイズ"""
dangerous_chars = ['<', '>', '"', "'", '&', '\\']
result = value
for char in dangerous_chars:
result = result.replace(char, '')
return result.strip()
# 使用例
token = SecurityUtils.generate_token()
hashed, salt = SecurityUtils.hash_password("my_password")
is_valid = SecurityUtils.verify_password("my_password", hashed, salt)セキュリティチェックリスト
- 全ての入力値がバリデーションされている
- 機密情報がログに出力されていない
- HTTPS が強制されている
- CORS ポリシーが適切に設定されている
- 依存パッケージの脆弱性スキャンが実施されている
- エラーメッセージに内部情報が含まれていない
FAQ
Q1: このトピックを学ぶ上で最も重要なポイントは何ですか?
実践的な経験を積むことが最も重要です。理論だけでなく、実際にコードを書いて動作を確認することで理解が深まります。
Q2: 初心者がよく陥る間違いは何ですか?
基礎を飛ばして応用に進むことです。このガイドで説明している基本概念をしっかり理解してから、次のステップに進むことをお勧めします。
Q3: 実務ではどのように活用されていますか?
このトピックの知識は、日常的な開発業務で頻繁に活用されます。特にコードレビューやアーキテクチャ設計の際に重要になります。
まとめ
| ツール | 特徴 | 用途 | 速度 |
|---|---|---|---|
| find | 標準搭載、高機能、POSIX互換 | 複雑な条件検索、スクリプト | 中 |
| fd | 高速、簡潔、.gitignore尊重 | 日常的な検索、開発作業 | 高 |
| locate | データベース検索 | ファイル名の素早い検索 | 最速 |
| which | PATH 検索 | コマンドの場所確認 | 即時 |
| whereis | バイナリ+man 検索 | コマンド関連ファイル確認 | 即時 |
| type | bash 組み込み | コマンドの種類確認 | 即時 |
| fzf | ファジー検索 | 対話的なファイル選択 | 即時 |
| tree | ツリー表示 | ディレクトリ構造の可視化 | 中 |
選択フローチャート
ファイルを探したい
│
├─ ファイル名だけ分かっている → locate(高速)
│
├─ 開発プロジェクト内の検索
│ ├─ シンプルな検索 → fd(.gitignore 尊重、簡潔)
│ └─ 複雑な条件 → find(論理演算子、-exec)
│
├─ システム全体の検索
│ ├─ 条件が名前だけ → locate
│ └─ サイズ/日時/権限も → find
│
├─ コマンドの場所 → which / type
│
└─ 対話的に選びたい → find/fd | fzf
13. find の高度なセキュリティ活用
13.1 ファイルシステムセキュリティ監査
# SUID/SGID ビットが設定されたファイルの検出(権限昇格のリスク)
find / -type f \( -perm -4000 -o -perm -2000 \) -ls 2>/dev/null
# ワールドライタブルなファイルの検出
find / -type f -perm -0002 -not -path "/proc/*" -not -path "/sys/*" 2>/dev/null
# 所有者のないファイル(ユーザーやグループが削除されている)
find / -nouser -o -nogroup 2>/dev/null | head -50
# 最近変更された設定ファイルの確認(侵入の痕跡)
find /etc -type f -mtime -1 -ls 2>/dev/null
# 隠しファイルの検出(ドットファイル、不審なファイル)
find / -name ".*" -type f -not -path "/home/*" -not -path "/root/*" 2>/dev/null | head -50
# 実行可能ファイルで最近作成されたもの
find /tmp /var/tmp /dev/shm -type f -executable -newer /etc/hostname 2>/dev/null
# 不審な cron ジョブの検出
find /etc/cron* /var/spool/cron -type f -ls 2>/dev/null
find / -name "*.cron" -o -name "crontab" 2>/dev/null
# 大きすぎるログファイルの検出(DoS対策)
find /var/log -type f -size +100M -exec ls -lh {} \; 2>/dev/null次に読むべきガイド
参考文献
- Barrett, D. "Efficient Linux at the Command Line." Ch.4, O'Reilly, 2022.
- Shotts, W. "The Linux Command Line." 2nd Ed, Ch.17-18, 2019.
- GNU Findutils Manual. https://www.gnu.org/software/findutils/manual/
- fd GitHub Repository. https://github.com/sharkdp/fd
- fzf GitHub Repository. https://github.com/junegunn/fzf
- mlocate / plocate Documentation. https://plocate.sesse.net/