find的使用方法和典型使用场景

2026-08-16

find的使用方法和典型使用场景

1.磁盘占用过高问题定位

最近在学习一些关于服务器磁盘方面的运维知识,正好发现我的一台机器上根目录磁盘占用达到了96%,这个占用量已经非常危险了

首先使用 df -h 查看磁盘占用率:

文件系统        大小  已用  可用 已用% 挂载点
tmpfs           1.6G  2.8M  1.6G    1% /run
/dev/nvme0n1p4   57G   52G  2.3G   96% /
tmpfs           7.7G     0  7.7G    0% /dev/shm
tmpfs           5.0M  4.0K  5.0M    1% /run/lock
/dev/nvme0n1p2  1.9G  287M  1.5G   17% /boot
/dev/nvme0n1p5   27G  7.8G   18G   31% /home
/dev/sda        916G  229G  641G   27% /data

接下来结合最近学习的内容,来说一说解决思路和方案。

新手的最常见的做法往往是 cd 进各个目录,挨个 du -sh * ,一层一层翻下去,运气不好就能翻很长时间,最后也找不到占用磁盘的大文件到底在哪里。

而真正熟悉 find 的人,只会敲一行:

find / -xdev -type f -size +500M 2>/dev/null 

等待片刻后,所有超过500MB的大文件全部列出来,占用磁盘的元凶一目了然。

除了磁盘占用高的故障,find 还有很多日常运维工作中排障用法。

2.find到底做了什么

find 的核心逻辑非常简单:从某个目录开始,遍历它和它所有子目录中的每一个文件和目录,按你给的条件挑出符合条件的部分。

基本语法:

find <路径> <条件>

比如:

find /var/log -name "*.log"

意思是:从 /var/log 开始,递归查找所有文件名以 .log 结尾的文件。如果不写任何动作,GNU find 默认会执行 -print ,也就是把结果打印出来。所以其实大多数人都在用省略 -print 的简化写法。

这个过滤功能看起来和 grep 有点像,但本质完全不同:grep 搜的是文件内容,find 搜的是文件本身(文件名、类型、大小、时间、权限、归属)。所以在排障时经常会见到 findgrep 配合使用。

3.find核心能力

  1. 按文件名查找:-name / -iname

    find /etc -name "*.conf"
    find /etc -iname "*.CONF"
    

    -name 区分大小写,-iname 不区分大小写(ignore)。

    这里再补充两个高频写法:

    排除某种文件,用 ! (逻辑非):

    find . -name "*test*" ! -name "*.log"
    

    意思是:找出文件名包含 test 的文件,但排除掉 .log 结尾的文件。很多人在使用过程中会忽略 find 本身的排除功能。但是这里需要注意一点,一个 ! 只能管它后面的1个条件,如果还需要一个排除条件,就需要多一个!

  2. 按文件类型查找:-type

    只找目录:

    find /opt -type d
    

    只找普通文件:

    find /opt -type f
    

    只找符号链接:

    find /opt -type l
    

    排障时经常要确认一个文件到底是目录还是文件还是软链接,-type 直接筛查干净。

  3. 按时间查找:-mtime / -mmin

    这是日常运维中用得最多的条件之一。

    find /var/log -name "*.log" -mtime +7
    

    意思是:找出 /var/log 下,最后修改时间超过7天的日志文件。这里有个细节要注意:+7 表示超过七天,-7 表示7天以内,不带符号的 7 表示刚好第7天。日常用的比较多的是 +n ,用来清理过期日志

    find /var/log -name "*.log" -mtime +7 | xargs rm 
    

    如果要按照分钟算,比如排查最近10min被改动过的文件:

    find /etc -mmin -10
    
  4. 按大小找:-size

    find / -xdev -type f -size +500M
    

    +500M 表示大于500MB,-size 同样支持 + - 表示大于、小于。这里顺便说一下 -xdev :它的作用是只在当前文件系统内搜索,不跨到其它挂载点。如果从 / 开始搜索却不加 -xdevfind 会一路钻进 /proc/sys这些虚拟文件系统,不仅慢,还会出一堆没有意义的结果,外加大量权限不足的报错。可以在命令后面加上 2>/dev/null 把错误信息丢掉,只看正常结果:

    find / -type f -size +500M 2>/dev/null
    

    如果不光想知道有哪些大文件,还想按大小排序,列出占用最多空间的几个,可以配合 -exec dusort

    find / -xdev -type f -size +100M -exec du -h {} \; 2>/dev/null | sort -rh
    

    du -h {} 把每个匹配文件的实际大小算出来, sort -rh 按人类可读的大小做降序排列,这样输出的就是从大到小排好序的列表,比单纯列文件名更直观。

  5. 按权限找:-perm

    find / -perm -4000 -type f 2>/dev/null
    

    据说这个命令是一条经典的安全审计命令,用来找出所有设置了SUID权限位的文件,这类文件执行时会临时获得文件所有者(通常是root)的权限,如果被恶意利用,就是一个常见的权限提升突破口,所以安全排查、入侵检查时经常用到这条命令。

  6. 对找到的结果执行命令:-exec

    这是 find 命令最强大的能力,找到文件之后,直接对它们执行命令,不用再手动复制粘贴文件名:

    find /var/log -name "*.log" -mtime +30 -exec rm {} \;
    

    {} 是占位符,代表当前匹配到的文件;结尾的 \; 表示每找到一个文件,就单独执行一次命令,这种写法的问题是:如果匹配到了1000个文件,就会启动1000次的 rm 进程,效率比较低。更高效的写法是用 + 结尾:

    find /var/log -name "*.log" -mtime +30 -exec rm {} +
    

    + 会把尽可能多的文件名打包,合并成尽量少的命令调用次数,效果上更接近 xargs 的批处理方式,速度明显更快。

    find /var/log -name "*.log" -mtime +30 | xargs rm
    

    这里建议任何带 -exec rm 或者 -delete 的命令,第一次执行前,先把删除动作换成 -print,确实列出的文件都是需要删除的,然后再加删除动作,可以避免在排障过程中又制造故障 。

    find /var/log -name "*.log" -mtime +30 -print
    

    -exec 还有一个很常见的组合用法,就是配合 grep 一起用,相当于把找文件和找内容链接到一起:

    find . -name "*.cc" -exec grep -l "void main" {} \; 
    

    意思是,先用 find 把所有 .cc 文件找出来,再对每一个文件用 grep 看里面是否包含 void main 这段代码。这也就是为什么 findgrep 是最常见的搭档关系,一个负责定位文件范围,一个负责定位内容。

  7. 内置删除:-delete

    find /tmp -name "*.tmp" -mtime +1 -delete
    

    -deletefind 自带的删除动作,效果类似 -exec rm {} \; ,但执行效率更高,不需要额外启动 rm 进程。执行同样需要注意安全,建议先不带操作跑一遍,合适后再执行删除。

  8. 排除某些目录:-prune

    排障或者搜代码的时候,经常不想搜进 .gitnode_modules 这种目录,用 prune 可以直接跳过:

    find . -path "./node_modules" -prune -o -name "*.js" -print
    

    拆解一下这条命令,-path "./node_modules" -prune 表示如果路径匹配到 node_modules 就不要继续往下钻;后面的 -o -name "*.js" -print 表示,否则的话,按照规则 *.js 匹配并打印。-o 表示两个互斥的条件,配合 -prune 实现跳过某个目录,但正常处理其他部分的效果。

  9. 配合 xargs 使用

    find . -name "*.bak" -print0 | xargs -0 rm
    

    直接用 find ... | xargs rm 在文件名包含空格或特殊字符时会出错,因为 xargs 默认按空白符切割参数。-print0 会让 find\0 而不是换行符分隔文件名,配合 xargs -0 使用,可以安全处理任何带空格、换行的奇葩文件名,这是处理批量文件操作时的标准安全写法。

4.性能优化:为什么有时候 find 会让服务器卡住

find 是实时遍历文件系统,目录越深、文件越多,扫一遍花的时间就越长。这里有两个真实场景:

场景1:定时任务里的 find 拖垮了服务器

有的公司会在 crontab 中设置定时清理任务,每天清理 /tmp 下一天前产生的大量临时文件,结果每次任务一跑,服务器负载会跟着飙升,影响其他服务。原因就是 find 要遍历的文件数量太大,加上后面如果又配合了低效的 -exec ... \; 逐个删除,进程开销叠加在一起对系统的瞬时压力很明显。

优化思路也比较直接:

find /tmp -maxdepth 1 -type f -mtime +1 -delete

-maxdepth 1 限制只搜索第一层目录,不再往更深的子目录钻。因为很多场景下,其实不需要无限递归,使用这个限制可以大幅度减少要扫描的文件数量;同时把删除动作换成内置的 -delete ,而不是逐个调用外部的 rm ,进一步降低开销。

场景2: 从根目录搜索时,意外扫到了网络文件系统

如果服务器挂载了 NFS(网络文件系统),从 / 开始的 find 默认会一路扫进这些网络挂载点,而网络文件系统的访问速度通常远低于本地磁盘,一旦扫进去,耗时会成倍增加。这种情况下,前面提到的 -xdev 同样能帮上忙——它不仅能跳过 /proc/sys,也能避免跨到其他挂载的文件系统(包括网络存储)。

对于一个数据量很大的目录来跑 find ,如果不急着要结果,可以加 & 放后台执行,避免占着终端干等。

5.一个真实的排障过程:磁盘爆满复盘

第一步,找出根分区下的大文件

find / -xdev -type f -size +500M 2>/dev/null

发现以下文件:

/root/.cache/pip/http-v2/5/5/6/4/a/5564a871319d42ec90c363f7100f58767ded8bcadf47ba562e43ec00.body
/var/lib/snapd/cache/bece47eaffcab46af8b7ec79322cdf6d6aa8f3ffaaa5b1f51e4dcec1333e33b6840775d7fbc4736d74ddfcbec1e8d58a
/var/lib/snapd/cache/0e99938fd412cec5852ed680bcbc536e1929ff31315a57224aa7cb03867f2505bada1452bcbe1ac6c9f2e35101c4612f
/var/lib/snapd/cache/2b4539cb4dfdb33019d64567982948db646776d113d2df5424dcf6fe9badc91d5cff61e88c01c8a01e53a5925b2def54
/var/lib/snapd/cache/6b8f0b8920d12519d7c5e677950a231810fa253e34962f312b3dfa58af9d91eeab9d5c1b3483cf0da6df66f23f3f5f8d
/var/lib/snapd/snaps/gnome-42-2204_247.snap
/var/lib/snapd/snaps/gnome-46-2404_164.snap
/var/lib/snapd/snaps/gnome-46-2404_153.snap
/var/lib/snapd/snaps/gnome-42-2204_263.snap

第二步,确认这些文件是不是真的没用了

find /var/lib/snapd -type f -mtime +30 2>/dev/null

加上 -mtime +30,确认这些大文件已经 30 天没被修改过,基本可以判断是历史遗留的无用文件,而不是正在使用的数据。

第三步,先打印确认,然后再 -delete

find /var/lib/snapd -type f -mtime +30 2>/dev/null
find /var/lib/snapd -type f -mtime +30 -delete

第四步,检查磁盘空间是否恢复

df -h /

整个过程没有手动进任何一个目录,全程靠 find 的条件组合完成定位、确认、清理三个阶段——这正是它被低估的地方:大家都知道它能”找文件”,但很少有人把它当成一个完整的排障加处理工具来用。

6.踩坑总结

  1. 忘记给通配符加引号: find . -name *.txt 几乎一定会出问题,一定记得是 find . -name "*.txt"
  2. 从根目录搜索不加 -xdev ,会一路钻进 /proc/sys,甚至跨到网络挂载的文件系统,慢且没意义,所以记得加上 -xdev ,同时用 2>/dev/null 屏蔽权限报错。
  3. -ctime 当成创建时间-ctime 是元数据变更时间,不是创建时间,这是一个普遍的误区。
  4. 直接执行 -delete-exec rm,不先 -print 确认,linux中删除是一个不可逆操作,所以一定要先打印结果确认,再补上删除动作,啥急事也急不了这几分钟,养成这个结果可以避免很多事故。
  5. -exec ... {} \; 处理大批量文件,会逐个启动 rm 进程,效率很低, 能用 -exec ... {} + 或者配合 xargs 就尽量用。
  6. 大范围搜索不加 -maxdepth,明明只需要搜第一层目录,却让 find 递归深钻,纯属浪费时光。

7.运维专业的高频面试题,感受一下

服务器上有一个 .logs 目录,如何删除该目录下最后一次访问时间超过1年的文件?

find .logs -type f -atime +365 -print
find .logs -type f -atime +365 -exec rm {} +
find .logs -type f -atime +365 -delete

这道题同时考察了3个知识点,-type 区分文件和目录、-atime 来按访问时间筛选结果、-print 确认结果后再删除的安全习惯。

8.写在最后

很多服务器是不提供图形界面的,就算有界面,如果不提供 FTP服务的话,那么关于文件系统的所有访问都只能在终端里进行。以前刚接触这一块的时候,找文件都是 cd + ls 古法查找,慢慢熟练的以后,就算知道有 find 这么个工具,也是用得不多。但是随着接触的业务量级越来越大,动辄就是上百G、几个T的文件,这时候再用老办法基本是完成不了工作的,所以还是得与时俱进。

而且技术手段这东西更新太快,现在不学会 find ,说不定又会被新的工具取代,每次一学新技术,就发现自己落后好几个版本,这可是要出大问题的。