Linux服务器跑久了最容易爆掉的分区往往不是你以为的那个而是根分区。我有一台跑了一年多的CentOS 7某天凌晨收到磁盘告警ssh上去一看df -h根分区已经 100% 占用。顺着du -sh /tmp查下去里面堆了 7 个多 G 的临时文件来源千奇百怪安装包、解压残留、程序崩溃留下的中间文件、七个月没人碰过的日志。手动rm -rf /tmp/*当然能解决但治标不治本。真正的问题是系统能不能自动把过期的临时文件清掉这就引出了今天要聊的 tmpwatch 命令。这篇是 Linux 文件管理实操系列的第一篇适合做运维、管服务器、以及刚接触 Linux 命令大全想系统补补基本功的朋友。读完你不仅能跑通 tmpwatch还能搞明白它到底是什么原理、有哪些坑、以及为什么有些文件就是删不掉。1. 为什么系统里会莫名多出这些临时文件1.1 临时文件都堆在哪儿Linux 约定俗成的临时目录主要是两个/tmp和/var/tmp。两者定位略有不同/tmp偏向短生命周期数据重启后经常被清空很多发行版甚至直接把它挂成 tmpfs 跑在内存里/var/tmp偏向相对长时间保留的临时数据重启后依然存在给那些可能要跨重启继续使用的程序准备。但真正让磁盘爆掉的往往不是系统规范而是人的使用习惯和不靠谱的软件行为。我见过最常见的几类堆积安装包和解压中间产物tar -xzf xxx.tar.gz -C /tmp之后忘了清。Java 运行时的 hsperfdata 目录每次启动 JVM 都会在/tmp下留下/tmp/hsperfdata_用户名退出不干净就一直积着。下载类程序的分片缓存部分下载工具、网盘客户端会在/tmp或用户缓存目录里写大文件。日志和崩溃转储应用程序把临时日志写到/var/tmp然后没人管。编辑器交换文件vim 异常退出后留下的.swpdist-upgrade 之类的包管理器临时文件。这些文件单个看都不大堆几个月就是几 G 到几十 G。尤其/var/tmp藏在系统目录里平时不会注意一查吓一跳。1.2 手动清理的三大困局面对堆积的临时文件很多人第一反应是rm -rf /tmp/*。但这条命令在真实环境里问题不少。第一/tmp目录带了 sticky bit权限 1777非 root 用户手动清理时只能删自己拥有的文件会刷出一堆Operation not permitted部分文件删不干净还得补一条sudo。用 root 删倒是能删完但误删正在使用的临时文件风险极高。第二/tmp/*这个通配符不匹配隐藏文件.X11-unix、.XIM-unix这类带点的目录根本不会进入你的删除范围。你以为清干净了实际上 socket 和锁文件还在甚至可能影响 X11 的权限判断。第三rm -rf没有任何“过期时间”概念它只会按文件列表删不会判断文件多久没被访问。你要的明明是“清理 10 天前的临时文件”结果把所有当前正在用的会话缓存也一起扬了。也有人改用find /tmp -atime 7 -delete。这个命令有进步至少带了时间判断但它有两个绕不开的缺陷遇到非空目录会报错跳过删除符号链接时如果你没想清楚“链接本身 vs 链接指向的目标”很容易误伤。逻辑一复杂命令就越写越长find /tmp -atime 7 -delete \ -not -path /tmp/.X11-unix/* \ -not -path /tmp/hsperfdata_* \ ...这段命令长不说逻辑正确性还得自己负责。而 tmpwatch 就是专门为这个场景设计的工具把“递归清理”“空目录删除”“符号链接安全”“排除路径”这些经验固化成默认行为一条简单命令就能表达清楚。1.3 谁最需要这篇如果你管着几台 Linux 服务器或者自己开发机的/tmp经常爆那 tmpwatch 属于早晚要会的命令。它不复杂但理解它背后的清理逻辑比记住参数更有价值。2. tmpwatch 的核心逻辑它到底按什么规则删文件2.1 三种时间戳为什么默认看 atimetmpwatch 命名很有意思tmp watch盯着临时目录看家。它默认做的事情是扫描指定目录里的文件把“一段时间内没有被访问过”的文件删掉。这里的关键词是“没有访问过”不是“没有修改过”。Linux 文件系统里每个文件都维护三个时间戳很多刚入门 Linux 命令大全的朋友分不清时间戳全称触发条件比喻atimeaccess time文件被读取时更新最后一次有人进你家门mtimemodify time文件内容被修改时更新最后一次有人动了你家的家具ctimechange time文件属性或内容变化时更新包括权限、硬链接数最后一次有人改了你家的门牌tmpwatch 默认按 atime 判断过期这个设计很聪明。临时文件的核心价值是“可能需要被复用”如果一个文件长达几十天没人读基本可以断定它已经失去了存在意义。相反mtime 近不代表文件活跃很多临时文件写完就没人再管mtime 停在创建那一刻但它们其实早就该被清理了。不过 atime 有一个众所周知的缺陷现代 Linux 默认挂载参数是relatimeatime 不会每次读取都更新只有 atime 早于 mtime 或 ctime、或者 atime 距离上次更新超过 24 小时才会刷新。所以按 atime 清理在某些场景下会“误伤”近期读过但没触发 atime 更新的文件这也是我在第 5 章要重点说的坑。如果你不想按 atime 判断tmpwatch 也给了显式参数-u按 atime默认-m按 mtime适合清理“长时间没有被修改”的文件-c按 ctime适合关注文件元数据变化2.2 目录、符号链接这些特殊类型怎么处理tmpwatch 和rm -rf最大的区别之一是它把目录和符号链接当成了“需要专门策略的对象”而不是无脑删除。先说目录。tmpwatch 默认会递归遍历指定目录下的所有子目录这是它默认行为的一部分不需要额外加参数。删文件时它会逐层判断如果一个目录本身也超过过期时间并且目录里已经没有未过期的文件tmpwatch 会把这个空目录一并删除。如果你不希望它动任何目录可以用-d或--nodirs明确禁止。再说符号链接。tmpwatch 看到符号链接时删除的是符号链接本身而不是链接指向的目标文件。这个行为非常重要因为/tmp下经常有人放指向别的分区的软链接如果工具傻乎乎地跟着链接去删目标很容易删挂别的服务。tmpwatch 在这点上的默认处理是安全的。2.3 两个反直觉的例子第一刚写入的文件也可能被删。比如你的程序凌晨 4 点往/tmp/xxx.tmp写了一份数据之后就没再读它。如果这时 tmpwatch 按 atime 清理而这个文件的 atime 早于阈值它会直接被删掉哪怕文件是 10 分钟前刚写好的。因为 tmpwatch 考察的是“访问时间”而不是“写入时间”。第二删完之后父目录还在。如果你看到/tmp/somedir一直没被删别急着怀疑 tmpwatch 没干活。它可能已经把/tmp/somedir下的过期文件全清了但目录本身还没过期或者目录里还有别的未过期文件导致目录不能被判定为空。find /tmp/somedir -type f | wc -l一看便知。3. 实操一条命令清理一周前的临时文件3.1 先装好工具RHEL/CentOS/Fedora 系默认仓库就有安装很快# RHEL/CentOS yum install -y tmpwatch # 或 dnf install -y tmpwatchDebian/Ubuntu 系的 tmpwatch 包已经不怎么维护了一般用功能兼容的tmpreaper替代apt install -y tmpreapertmpreaper 的命令行参数和 tmpwatch 基本对齐但它多了--protect白名单功能后面我单独讲。如果你是在 macOS 或其他 Unix 上折腾大概率没有这个工具那就得靠 find 或者顺手写个 systemd timer 了。3.2 语法与常用参数速查tmpwatch 的基本格式是tmpwatch [选项] 过期时间 目录...过期时间默认单位是小时也支持s秒、m分钟、h小时、d天后缀。所以tmpwatch 24 /tmp和tmpwatch 24h /tmp等价tmpwatch 10d /tmp表示清理 10 天未被访问的文件。常用参数我整理成了表参数作用-u按 atime 判断默认-m按 mtime 判断-c按 ctime 判断-d不删除目录即使目录已空-f强制删除只读文件-q安静模式不输出删除文件列表-t测试模式只打印将要删除的文件不实际删除-x排除指定路径可多次使用-a清理所有文件类型包括管道、套接字等特殊文件3.3 千万不要跳过测试模式我刚用 tmpwatch 时最遗憾的事就是没有早点养成先跑-t的习惯。测试模式不会删任何文件只是把“如果我现在执行会删掉哪些文件”打出来。这是 tmpwatch 给的最便宜的保险。tmpwatch -t 10d /tmp输出会列出一长串文件路径耐心看一遍这些路径确认没有当前正在用的目录再决定要不要动真格。更严谨的做法是把测试输出存下来对比一下数量级tmpwatch -t 10d /tmp /tmp/tmpwatch_test.log wc -l /tmp/tmpwatch_test.log如果这个文件数量正常再执行真实清理。不要担心测试模式影响性能tmpwatch 的-t同样会走完整的遍历逻辑只是最后一步改成打印而已。3.4 三条可以直接抄的实战命令第一条最常用的按 atime 清理tmpwatch -q 168 /var/tmp这个命令清理/var/tmp下 7 天168 小时未被访问的文件-q让输出干净一点适合放到 cron 里跑而不刷日志。第二条按 mtime 清理tmpwatch -m -q 72 /tmp这条适合那些“文件被修改之后就再也不读”的场景。比如 Java 进程崩溃产生的 hsperfdata、解压了一半的安装包用 mtime 判断比 atime 更准。第三条排除关键目录再清理tmpwatch -q 240 /tmp -x /tmp/.X11-unix -x /tmp/.XIM-unix -x /tmp/.font-unix -x /tmp/hsperfdata_*这是 CentOS 官方 cron 脚本里真实在用的写法。X11 相关套接字目录虽然临时但删了会影响图形界面会话必须排除。Java 的 hsperfdata 目录虽然也是临时文件但在 JVM 运行期间删掉会导致监控数据异常也排除掉。这条命令很有代表性它告诉你 tmpwatch 不是一味猛删而是可以通过-x反复指定白名单。3.5 验证清理效果执行完之后确认三件事du -sh /tmp find /tmp -atime 240 -type f | wc -l ls -ld /tmp/.X11-unix第一行看整体空间是否释放第二行看过期文件是否归零第三行确认被排除的目录还在。这三条都通过这轮清理才算真正干完。4. 接入定时任务让清理自动跑起来4.1 用 crontab 做周期清理手动执行 tmpwatch 只能救一时真正省心的是让它定时跑。最朴素的方式就是 crontab。先确认 tmpwatch 的绝对路径which tmpwatch然后编辑 root 用户的 crontabcrontab -e加入一行15 3 * * * /usr/sbin/tmpwatch -m -q 168 /var/tmp /var/log/tmpwatch.log 21这行表示每天凌晨 3 点 15 分按 mtime 清理/var/tmp下 7 天未修改的文件日志写到/var/log/tmpwatch.log。选凌晨跑是因为这个时间段业务负载最低临时文件被占用的概率也小。日志重定向很重要否则 cron 默认会把 stdout 用邮件发给你服务器不配邮件的话就直接吞掉了。4.2 发行版自带的周期清理脚本如果你用的是 CentOS/RHEL其实系统已经内置了一套 tmpwatch 定时任务。在/etc/cron.daily/下有个tmpwatch脚本内容就是一连串精心调整过的 tmpwatch 命令。不同版本略有差异但典型逻辑是这样flags-umc /usr/sbin/tmpwatch $flags -x /tmp/.X11-unix \ -x /tmp/.XIM-unix -x /tmp/.font-unix \ -x /tmp/.ICE-unix -x /tmp/.Test-unix \ -X /tmp/hsperfdata_* -q 240 /tmp /usr/sbin/tmpwatch $flags -q 720 /var/tmp这套脚本有两个细节值得学习。一是它区分了不同目录的过期阈值/tmp给 240 小时10 天/var/tmp给 720 小时30 天因为后者本来就是给相对长期数据用的。二是它对特殊目录做了细致排除X11 的套接字目录、Java 的 hsperfdata 目录都被保护起来。如果你想自定义策略不用改系统自带脚本直接在/etc/cron.daily/下新建一个独立脚本或者用 crontab 覆盖即可。注意系统自带脚本的名字可能与你的自定义脚本冲突建议用前缀数字控制执行顺序比如10-clean-tmp.sh。4.3 怎么确认它真的跑了定时任务最大的问题是“不响”。如果你不主动看它执行成功或失败你都不知道。我的习惯是在清理脚本里加一个简单的统计逻辑#!/bin/bash before$(du -sm /var/tmp | cut -f1) /usr/sbin/tmpwatch -m -q 168 /var/tmp after$(du -sm /var/tmp | cut -f1) echo $(date %F %T) freed $((before - after)) MB /var/log/tmpwatch.log这样每天扫一眼日志就能看到实际释放了多少空间。如果连续几天都是 0 MB说明临时文件堆积速度慢或者阈值设得太宽可以适当缩小时长。5. 坑与边界为什么有时候文件没被删掉5.1 relatime 让 atime 失真我在第 2 章提过现代 Linux 默认用relatime挂载选项atime 不是每次读取都更新。这意味着一个文件虽然今天被读过但如果它在 24 小时内已经被 atime 更新过或者它的 atime 仍然早于 mtime这次读取就不会改变 atime。tmpwatch 按 atime 清理时看到的是一个“假的过期时间”可能会把最近还在用的文件判定为过期。你可以用mount | grep /tmp 或stat /tmp查看挂载选项。如果确实开了 relatime而你的临时目录又有“频繁读但不频繁写”的场景建议直接改用-m按 mtime 判断或者接受 atime 的粗糙并调大阈值。5.2 正在使用的文件一样会被 unlink这是 tmpwatch 最容易踩的重大事故点。很多人的直觉是“文件正在被进程占用删除应该会失败”。但在 Linux 里删除一个打开的文件并不会失败它只是把这个文件从目录中 unlink进程持有的文件描述符依然指向原来的 inode数据还在进程读写不受影响。问题出在路径已经不存在了。如果某个程序的行为是“启动时先检查/tmp/xxx.lock是否存在不存在就创建一个”而它启动后不再持有这个文件那么 tmpwatch 把这个锁文件删掉后另一个程序进程再启动就会发现锁“消失”了可能会引发重复启动或数据竞争。我见过最典型的场景是数据库把临时表空间放在/tmptmpwatch 按 10 天清理正好把几个月没重启的数据库的临时文件给删了数据库后面需要扩展临时表空间时发现路径没了直接报错。所以请记住tmpwatch 只负责“过期”不负责“是否被使用”。在关键目录上跑清理先lsof D /tmp 2/dev/null | head看一眼有没有重要进程持有文件再决定阈值。5.3 粘滞位和普通用户权限/tmp目录的权限是 1777sticky bit 保证了普通用户只能删除自己拥有的文件。如果你的 tmpwatch 不是以 root 身份执行的清理时会遇到大量Permission denied然后跳过这些文件。很多初学者在自己家目录跑tmpwatch 24 /tmp发现没删几个文件就是因为权限不够。解决方法是把定时任务放到 root 的 crontab 里或者用 sudo 执行。反过来如果你故意想用普通用户清理自己的临时文件也不需要处理别人的文件直接指定自己的目录即可tmpwatch -m 24 ~/tmp5.4 挂载点与跨文件系统风险有些环境会做这样的规划/tmp/sub单独挂载了一块数据盘作为某个应用的中间产物目录。当 tmpwatch 递归清理/tmp时它不会因为你挂了个独立分区就停下它会跨进这个挂载点把子分区里的过期文件一并清掉。这不是 tmpwatch 的 bug递归清理的语义就是这样。但对你来说这可能是惊吓。解决办法是明确排除挂载点tmpwatch -q 240 /tmp -x /tmp/sub或者在规划目录结构时把需要独立挂载的目录放到 tmpwatch 管理范围之外比如/opt/app/tmp。5.5 一个完整的排错链路假设你配置了 crontab 自动清理结果第二天发现/tmp还是满的。别直接怀疑 tmpwatch 没安装按这个顺序排查第一步确认定时任务真的有在跑grep tmpwatch /var/log/cron第二步手动跑一次测试模式tmpwatch -t 168 /var/tmp | head -50第三步如果测试模式能看到文件说明命令本身没问题问题出在权限、路径或日志重定向上。如果测试模式什么都不输出说明目录里确实没有到达阈值时间的文件那就要检查是不是所有文件都在被持续访问或者阈值设得太长。第四步查一下挂载参数mount | grep -E /tmp| / stat -f /tmp看到relatime标志你就知道为什么按 atime 判断会失灵了。6. 文件系统受限时的替代方案6.1 tmpreaper更安全的 Debian 系替代tmpreaper 是 tmpwatch 的增强版Debian/Ubuntu 上一般直接用它。它的核心参数和 tmpwatch 兼容保留了-m、-d、-t这些常见选项但多了几个值得关注的功能。一个是--protect正则白名单可以保护匹配的文件不被动tmpreaper -m 168 --protect .*\.log$ /var/tmp另一个是--delay删除前等待几秒给用户 Ctrl-C 打断的机会。这个大招在交互式服务器上特别有用偶尔手滑跑了一条大范围清理还能救回来。tmpreaper -m 168 --delay 5 /var/tmp如果你在用 Debian/Ubuntu建议直接养成用 tmpreaper 的习惯不要再去装已经停止维护的 tmpwatch 包。6.2 systemd-tmpfiles现代发行版的默认选择现在新装的 Linux 发行版很多已经不依赖 cron tmpwatch 这套组合了而是用 systemd 自带的 tmpfiles 机制。你看/usr/lib/tmpfiles.d/和/etc/tmpfiles.d/下的.conf文件就能看到系统定义的临时目录策略。最典型的配置长这样d /tmp 1777 root root 10d这一行的意思是确保/tmp目录存在权限 1777属主 root超过 10 天的内容会被systemd-tmpfiles --clean清理。它和 tmpwatch 的核心逻辑一样只是把清理规则声明式地写进配置文件更适合自动化管理。手动触发一次清理systemd-tmpfiles --clean /etc/tmpfiles.d/tmp.conf如果你在 systemd 环境里做新项目优先用 tmpfiles 方案它内建 timer、不依赖 cron、也不会出现 cron 邮件问题。tmpwatch 适合存量机器和老运维习惯新环境已经可以平滑迁移了。6.3 三者的选型对比工具适用发行版清理基准优点缺点tmpwatchRHEL/CentOS/Fedoraatime/mtime/ctime老牌稳定一条命令即可运行维护停滞参数相对基础tmpreaperDebian/Ubuntuatime/mtime/ctime有 protect 和 delay 保护机制需要额外安装systemd-tmpfiles所有 systemd 发行版配置文件 age 字段声明式配置与 systemd 集成学习成本在配置文件语法我的建议很直接存量 RHEL 系机器继续用 tmpwatch 没问题它是系统自带、有发行版维护的Debian/Ubuntu 机器用 tmpreaper安全机制更完善新装机或者容器环境优先研究 systemd-tmpfiles它才是现在和未来的主流。最后分享一个我在实际运维中的小习惯不管用哪种工具我都会在每月第一周的周一手动跑一次du -sh /tmp/* | sort -h | tail -20看看临时目录里谁是“空间大户”。如果某个目录连续两个月排在前三说明不是临时文件的问题而是某个程序的设计问题这时候就该去修程序的清理逻辑而不是单纯依赖 tmpwatch 兜底。工具能帮你处理 90% 的常规情况剩下 10% 的异常要靠你对业务的理解去补。