find —— 按条件找文件并直接处理

运维工具手册 03-文件与传输 6.4 KB 原文

find —— 按条件找文件并直接处理

分类:03 文件与传输 适用:全平台(GNU findutils;macOS 为 BSD find,参数有差异) 一句话:磁盘满了、权限乱了、要批量处理文件,find 是唯一不需要背复杂脚本的工具。

1. 它解决什么问题

  • 找出 7 天前的日志并删除。
  • 找出大于 1G 的文件,看谁占空间。
  • 批量改权限、批量改名、批量 grep。
  • 找属于已删除用户的文件、找 SUID 文件(安全审计)。

2. 基本结构

find <起点> <条件> <动作>
find /var/log -name '*.log' -mtime +7
find / -type f -size +1G 2>/dev/null

默认动作是 -print,所以不写动作就输出路径。

3. 常用条件

按名字/路径

find . -name '*.conf'                 # 区分大小写
find . -iname '*.CONF'                # 忽略大小写
find . -path '*/nginx/*.conf'         # 路径匹配(含通配)
find . -regex '.*\.\(jpg\|png\)$'     # 正则(整路径匹配)
find . -not -name '*.log'             # 排除
find . -name '*.log' ! -name 'access*'

按类型

find . -type f          # 普通文件
find . -type d          # 目录
find . -type l          # 软链接
find . -type s          # socket
find . -type p          # 命名管道

按时间

find . -mtime -7        # 7 天内修改
find . -mtime +30       # 30 天前修改
find . -mmin -10        # 10 分钟内修改
find . -atime +90       # 90 天未访问
find . -ctime +180      # 180 天前元数据变更(权限/属主)
find . -newer /tmp/ref  # 比参考文件新
find . -newermt '2026-01-01'            # 指定时间之后
find . -newermt '2026-01-01' ! -newermt '2026-02-01'

-mtime n 的 n 是"24 小时块"计数,-mtime -1 表示不足 1 天,不是"今天"。要精确到分钟用 -mmin。

按大小

find . -size +100M      # 大于 100MB
find . -size -1k        # 小于 1KB
find . -size +0         # 非空文件
find . -empty           # 空文件/空目录

单位:c 字节、k KB、M、G。注意 -size 1M 表示"恰好 1M 的块",实际很少用,一般用 +/-。

按权限/属主

find . -perm 644                    # 权限恰好 644
find . -perm -644                   # 至少包含 644(rw-r--r--)
find . -perm /u+s                   # 任意 SUID
find . -perm -4000                  # SUID(安全审计)
find . -perm -o+w                   # 任何人可写(风险)
find . -user nginx
find . -group www
find . -nouser -o -nogroup          # 属主/属组已不存在

按数量/硬链接

find . -links +2                    # 硬链接数 > 2
find . -inum 131074                 # 按 inode(处理怪文件名)
find . -samefile /path/ref          # 同 inode

4. 控制遍历

-maxdepth 1             # 只看当前层(不递归)
-mindepth 2             # 跳过第一层
-xdev                   # 不跨文件系统(备份/清理必用)
-prune                  # 剪枝,配合 -path 排除目录

只扫当前目录:

find /var/log -maxdepth 1 -type f -name '*.log'

排除某目录:

find /data -path /data/cache -prune -o -type f -name '*.tmp' -print

顺序要点:-prune 所在分支后面必须用 -o 接实际条件,并显式 -print。

5. 动作

打印与格式化

find . -name '*.log' -ls                                  # 类似 ls -l
find . -type f -printf '%s\t%p\n'                         # 大小 + 路径
find . -printf '%TY-%Tm-%Td %TH:%TM %p\n'                 # 时间 + 路径
find /var/log -type f -printf '%s %p\n' | sort -nr | head # 最大的日志

-printf 常用占位:

占位 含义
%p 路径
%f 文件名
%s 字节大小
%u / %g 属主 / 属组
%m 权限(八进制)
%TY-%Tm-%Td 修改日期
%TH:%TM 修改时间
%i inode
%h 所在目录

执行命令

# 每个文件执行一次(慢,但兼容性好)
find . -name '*.tmp' -exec rm -f {} \;

# 批量传参(快,推荐)
find . -name '*.log' -exec gzip {} +

# 配合 sh -c 做复杂操作
find . -name '*.log' -exec sh -c 'echo "压缩 $1"; gzip "$1"' _ {} \;

# 批量改权限
find /var/www -type d -exec chmod 755 {} +
find /var/www -type f -exec chmod 644 {} +
find /var/www -type f -name '*.sh' -exec chmod 755 {} +

\; 与 + 的区别:

  • \; 每个文件启动一次进程,10000 个文件 = 10000 次 fork。
  • + 把所有文件一次性传给命令,快几十倍,但参数过长时会自动分批。

删除

find /tmp -type f -name '*.tmp' -mtime +7 -delete

-delete 必须放在最后,且会自动开启 -depth(先处理子项)。

更稳的写法(先看再删):

find /tmp -type f -name '*.tmp' -mtime +7 -print   # 先看清单
find /tmp -type f -name '*.tmp' -mtime +7 -delete  # 确认后删

管道给 xargs

find . -type f -name '*.log' -print0 | xargs -0 gzip -9
find . -name '*.java' -print0 | xargs -0 grep -l 'TODO'

必须用 -print0 + xargs -0,否则文件名含空格/换行会断裂。

6. 实战场景

6.1 磁盘满了,找大文件

find / -xdev -type f -size +500M -printf '%s\t%p\n' 2>/dev/null \
  | sort -nr | head -20 | awk '{printf "%.1fG\t%s\n", $1/1024/1024/1024, $2}'

-xdev 不跨挂载点,避免扫到 NFS/容器。

6.2 清理 30 天前日志(安全版)

# 1. 统计
find /var/log -type f -name '*.log' -mtime +30 | wc -l

# 2. 看总量
find /var/log -type f -name '*.log' -mtime +30 -printf '%s\n' | awk '{s+=$1} END {print s/1024/1024" MB"}'

# 3. 列出
find /var/log -type f -name '*.log' -mtime +30 -ls

# 4. 删除(先备份清单)
find /var/log -type f -name '*.log' -mtime +30 -print > /tmp/to_delete.txt
find /var/log -type f -name '*.log' -mtime +30 -delete

6.3 批量修权限(网站目录)

find /var/www/html -type d -exec chmod 755 {} +
find /var/www/html -type f -exec chmod 644 {} +
find /var/www/html -user root -exec chown www-data:www-data {} +

6.4 安全审计:找 SUID 与全局可写文件

find / -xdev -perm -4000 -type f -ls 2>/dev/null
find / -xdev -perm -o+w -type f -not -path '/proc/*' 2>/dev/null | head -50
find / -xdev \( -nouser -o -nogroup \) -ls 2>/dev/null

6.5 找出被删除但仍占空间的文件

find /proc/*/fd -lname '* (deleted)' -ls 2>/dev/null | head
# 更常用
lsof +L1

6.6 批量重命名(配合 shell)

find . -type f -name '*.JPG' -exec sh -c '
  for f; do mv -- "$f" "${f%.JPG}.jpg"; done
' _ {} +

7. 常见坑

  1. 通配符要加引号:-name *.log 会被 shell 先展开,必须 -name '*.log'。
  2. 2>/dev/null 不可省:扫 / 时权限报错会刷屏并拖慢。
  3. -delete 放错位置:放中间会把后续条件当动作,行为异常;永远放最后。
  4. -exec ... \; 性能极差:大目录用 +。
  5. -mtime 不是"自然日":-mtime -1 是 24 小时内,-mtime 0 同理。
  6. 不加 -xdev 会跨挂载点:可能扫进 NFS、Ceph、/proc,卡死或误删。
  7. -perm 644 是精确匹配,找"至少 644"要用 -perm -644。
  8. 文件名含空格/换行:-print0 + xargs -0,否则 rm 会删错。
  9. find 遍历顺序不保证,依赖顺序的逻辑要显式 sort。
  10. macOS 的 BSD find 不支持 -printf、-delete 部分用法,用 gfind(brew install findutils)。
  11. -newer 比较的是 mtime,要比较 ctime 用 -cnewer。
  12. -prune 与 -delete 冲突:-delete 隐含 -depth,-prune 失效,改写逻辑。

8. 速查表

find . -name '*.log'                        # 按名
find . -type f -size +100M                  # 大文件
find . -type f -mtime +30                   # 30 天前
find . -type f -mmin -10                    # 10 分钟内
find . -perm -4000 -type f                  # SUID
find . -empty                               # 空文件/目录
find . -maxdepth 1 -type f                  # 只看本层
find / -xdev -type f -size +1G 2>/dev/null  # 全盘大文件
find . -name '*.tmp' -print                 # 先看
find . -name '*.tmp' -delete                # 再删
find . -name '*.log' -exec gzip {} +        # 批量处理
find . -print0 | xargs -0 rm -f             # 安全管道
find . -type f -printf '%s\t%p\n' | sort -nr | head
find . -path ./cache -prune -o -type f -print
find —— 按条件找文件并直接处理 - 知识库 - 起航社区