find —— 按各种条件找文件
一句话定义
find 在文件系统里递归找文件——按名字、时间、大小、权限、所有者...等条件。比 ls + grep 灵活百倍。结合 -exec 或 xargs 可以对找到的文件批量操作。
典型场景
- 找最近修改的文件:
find /var/log -mmin -10 - 找大文件:
find / -size +1G - 清旧备份:
find /backup -name '*.bak' -mtime +30 -delete - 找空文件 / 空目录
- 找权限不对的文件:
find / -perm -4000(setuid) - 找属于某用户的文件
基本语法
find <path> [conditions] [actions]
find . # 当前目录所有
find /var/log # /var/log 下所有
find /var/log -name "*.log" # 按名字
按名字 / 路径
find . -name "*.log" # 按文件名(glob)
find . -iname "*.LOG" # ignore case
find . -name "test*" -o -name "*.bak" # OR
find . -path "*/node_modules/*" # 按完整路径匹配
find . -not -name "*.log" # 取反
find . ! -name "*.log" # 同上
find . -regex '.*\.\(log\|bak\)$' # 正则
按文件类型
find . -type f # 普通文件(**最常用**)
find . -type d # 目录
find . -type l # 软链接
find . -type b # 块设备
find . -type c # 字符设备
find . -type s # socket
find . -type p # pipe
按时间(实战最常用)
| 选项 | 含义 |
|---|---|
-mtime | 文件内容修改时间(按天) |
-atime | 文件访问时间(按天) |
-ctime | 文件元数据改变时间(按天,含权限、ownership 变化) |
-mmin / -amin / -cmin | 同上,按分钟 |
-newer file | 比 file 新 |
find . -mtime 0 # 今天改的(24h 内)
find . -mtime -7 # 7 天内改的
find . -mtime +7 # 7 天前改的
find . -mtime +30 -mtime -90 # 30 到 90 天之间
find . -mmin -10 # 最近 10 分钟
find . -newer /tmp/marker # 比 marker 文件新
# 一组组合
find /tmp -name "*.tmp" -mtime +1 # 1 天以上的 .tmp
-mtime -7是 "less than 7 days ago" = 最近 7 天内。+7是 "more than" = 超过 7 天前。-是近期、+是久远。
按大小
find . -size +1G # 大于 1GB
find . -size -1k # 小于 1K
find . -size +100M -size -1G # 100M 到 1G 之间
find . -size 0 # 空文件
单位:c(字节)、k、M、G、b(512 字节块,默认)。
排查"哪个文件占空间":
find / -xdev -size +1G -exec ls -lh {} \; 2>/dev/null | head
-xdev 不跨文件系统(避免进 /proc / /tmpfs)。
按所有者 / 权限
find . -user alice # 属于 alice
find . -group dev # 属于 dev 组
find . -nouser # 没归属(用户被删了)
find . -nogroup
find . -perm 644 # 权限正好 644
find . -perm -644 # 权限**至少**含 644 的位
find . -perm /222 # 权限**任一**含 220 等 group write 位
# 安全审计
find / -perm -4000 2>/dev/null # setuid 文件
find / -perm -2000 2>/dev/null # setgid
find / -perm -002 -type f 2>/dev/null # 全局可写文件(**安全风险**)
按深度限制
find . -maxdepth 1 # 只当前一层(不递归)
find . -maxdepth 2 # 最多两层
find . -mindepth 2 # 至少两层
find . -maxdepth 1 -type f # 当前层的所有普通文件
-maxdepth 1 等价 ls -A,但条件查询能力强得多。
组合:-and -or -not
find . -name "*.log" -and -mtime -1 # AND(默认)
find . -name "*.log" -o -name "*.bak" # OR
find . -not -name "*.tmp" # NOT
# 括号组合(要转义)
find . \( -name "*.log" -o -name "*.bak" \) -mtime +7
注意 ( ) 在 shell 里要转义为 \( \) 或加引号 '('。
行动 -exec / -delete / -print
-print(默认行为)
find . -name "*.log"
# 等价
find . -name "*.log" -print
-delete(危险)
find /tmp -name "*.tmp" -mtime +1 -delete # 删 1 天以上的 .tmp
# 删之前先 dry-run
find /tmp -name "*.tmp" -mtime +1 # 看会删什么
find /tmp -name "*.tmp" -mtime +1 -delete # 真删
必先
-delete——find 路径错了能删整个系统。
-exec command {} \;
find . -name "*.log" -exec ls -lh {} \; # 对每个找到的跑 ls
find . -name "*.bak" -exec rm {} \; # 删(等价 -delete 但慢)
find . -name "*.py" -exec wc -l {} \; # 统计行数
{} 是占位符(每次替换为找到的文件名)。\; 是命令结尾(必须转义)。
-exec command {} +
find . -name "*.log" -exec grep "ERROR" {} +
# 把多个文件一次性传给 grep(比 \; 快很多)
+ vs \;:
\;—— 每个文件单独执行一次命令(慢)+—— 多个文件一起执行(快,等价 xargs 行为)
+ 优先,除非命令不接受多文件参数。
-exec vs xargs
# -exec 方式
find . -name "*.log" -exec rm {} +
# xargs 方式(更快、更灵活)
find . -name "*.log" -print0 | xargs -0 rm
后者支持 -P 并行(见 xargs.md)。
实战例子
1. 清磁盘:找大文件
find / -xdev -type f -size +100M 2>/dev/null \
| xargs -I{} ls -lh {} \
| sort -k5 -hr | head
或者更整洁:
find / -xdev -type f -size +100M -exec du -h {} + 2>/dev/null \
| sort -hr | head
2. 清旧日志 / 备份
# 找 30 天以上的日志
find /var/log -name "*.gz" -mtime +30 -print
# 确认无误后
find /var/log -name "*.gz" -mtime +30 -delete
3. 找哪些 yaml 含某个关键字
find . -name "*.yaml" -exec grep -l "image:" {} +
# -l 只显示文件名
find . -name "*.yaml" -exec grep -Hn "imagePullPolicy" {} +
# -H 加文件名前缀、-n 加行号
4. 找最近改动的文件(排查改动来源)
find /etc -mtime -1 -type f # 最近 24 小时改的配置
find /etc -mmin -10 -type f # 最近 10 分钟
5. 找空目录 / 空文件
find . -type d -empty # 空目录
find . -type f -empty # 空文件
6. 找权限不对的私钥
find ~/.ssh -name "id_*" -not -name "*.pub" -perm /077 \
-exec ls -l {} \;
# /077 = 任何 group / other 可读 / 写 / 执行
# 私钥应该 600,否则 ssh 拒用
7. 找 K8s manifest 里某种资源
find . -name "*.yaml" -exec grep -l "kind: Deployment" {} +
8. 找 SUID 二进制(安全审计)
find / -perm -4000 -type f 2>/dev/null
# /usr/bin/passwd
# /usr/bin/sudo
# ...
-xdev 不跨文件系统
K8s 节点 /var/lib/kubelet/... 下有几千个 mount,find 进去会爆量:
find / -name "config.yaml" # 会进所有 mount
find / -xdev -name "config.yaml" # 只在根分区
通常 -xdev + 起点目录限制。或者用 --prune-mounts(新版 GNU find)。
提速:-prune 排除目录
# 找文件、但跳过 .git / node_modules / venv
find . \( -name .git -o -name node_modules -o -name venv \) -prune -o -name "*.py" -print
-prune 让 find 不进入那个目录。比"找完再过滤"快很多。
或者更现代:用 fd:
fd "\.py$" # 默认 respect .gitignore、快
但远端服务器不一定有 fd,find 永远在。
常见踩坑
坑 1:-mtime 单位是天、不是 24 小时
find . -mtime 0 # "今天"
-mtime 0 是 0-24 小时前,不是日历"今天"。8 点钟跑、能找到昨晚 10 点的(< 24 小时)。
-daystart 改成"日历天":
find . -daystart -mtime 0 # 今天 0 点到现在改的
坑 2:路径错 + -delete = 灾难
find / -name "*.tmp" -delete # 整个系统所有 .tmp
/ 起点 + 没 -xdev + -delete。
防御:
- 先
-print看一遍 - 路径写绝对、起点尽量小
- 不熟悉时用
-ok替代-exec(每次确认)
find /tmp -name "*.tmp" -ok rm {} \;
# < rm ... /tmp/a.tmp > ? ← y/n 确认
坑 3:-name 是 glob 不是正则
find . -name "test.*" # OK,glob 中 * 是 0+ 字符
find . -name ".*\.log" # ❌ glob 不认 \.
正则用 -regex:
find . -regex '.*\.\(log\|bak\)$'
坑 4:忘了引号让 shell 先展开
find . -name *.log # shell 先展开 → 当前目录有 a.log b.log 就变成 find . -name a.log b.log
find . -name "*.log" # ✅
find . -name \*.log # ✅
-name 后面的模式永远加引号。
坑 5:-exec 后忘了 \;
find . -name "*.log" -exec ls
# find: missing argument to '-exec'
\; 是命令结尾标记。或者用 +。
坑 6:含空格的文件名
find . -name "*.log" | xargs rm
# 文件 "a b.log" → xargs 切成 a 和 b.log
-print0 + xargs -0:
find . -name "*.log" -print0 | xargs -0 rm
或者用 -exec ... +:
find . -name "*.log" -exec rm {} +
坑 7:find . -name 大小写
find . -name "*.LOG"
# 找不到 a.log
加 -iname:
find . -iname "*.log"
坑 8:find 慢
find / -name file
# 慢死
- 路径太大 → 缩小起点
- 没
-prune排除大目录 - 进了 NFS / sshfs / 网络存储
加 -xdev 限制单文件系统、用 -prune 排除噪音目录、或换用 locate(基于索引):
locate file # 查 mlocate 索引(不是实时)
updatedb # 重建索引(cron 每天自动)
但 locate 索引可能 stale,不显示新文件。
坑 9:-mtime 和 -daystart 行为
find . -mtime +0 # 严格大于 0 天 = 24 小时以上
find . -mtime 0 # 0-24 小时
find . -mtime -1 # 同 -mtime 0
-mtime +0 是常被误解为"今天"——其实是"超过 24 小时"。
坑 10:find . -delete -empty 顺序问题
find . -type d -empty -delete
# 先删空目录、删完之后某些目录变空 → 但 find 不会重复扫描
要彻底删空目录:
find . -type d -empty -delete # 跑一次
find . -type d -empty -delete # 再跑一次
# 或者循环到没变化
替代 / 互补
| 工具 | 特点 |
|---|---|
find | 经典、最通用 |
fd | 现代替代、快、默认遵守 .gitignore |
locate / mlocate | 基于索引、快、但可能 stale |
rg --files | ripgrep 列出文件(含 gitignore 尊重) |
tree | 树形看目录结构 |
K8s 节点 / 远端服务器 find 永远在,学好。