Python pathlib 批量清理临时文件:后缀、修改时间和目录边界这样校验
清理构建缓存、导出中间文件或上传临时包时,真正危险的往往不是删文件这一行代码,而是筛选条件多写了一个目录、时间单位弄错,或者把待删文件直接处理后才发现范围过大。Python 的 pathlib 可以把路径解析、后缀判断和修改时间检查放在同一条可复核的流程里。
- 先用
Path.resolve()固定根目录,再用relative_to()检查候选项没有越过边界。 - 修改时间用秒级时间戳比较,示例只处理超过 7 天的
.tmp和.part文件。 - 先写出
cleanup-plan.txt清单,人工或流水线确认后再删除,失败时保留清单方便复盘。 - 删除阶段只接收已经通过筛选的绝对路径,不重新拼接用户输入的目录。
先把清理范围说清楚:删什么,不删什么
假设任务目录是 /srv/report-job/work,里面有临时压缩包、处理中间片段和最终产物。清理规则可以整理成下面这张小表:
| 规则 | 允许值 | 原因 |
|---|---|---|
| 根目录 | /srv/report-job/work | 只在任务工作区内操作 |
| 后缀 | .tmp、.part | 避开最终的 .zip 和报告文件 |
| 年龄 | 大于 7 天 | 给重试任务和人工下载留出缓冲时间 |
| 链接文件 | 跳过 | 避免跟随链接触碰工作区外的文件 |
这里的“7 天”不是通用安全常数。批处理每晚运行的场景可以按自身保留策略调整;第一次上线建议把阈值设得更长,并且初期只生成待删清单不执行实际删除。
用 pathlib 固定目录边界,再筛选后缀和 mtime
核心函数先返回所有候选文件,而不是直接执行删除。这样筛选过程可以单独抽出来测试,删除动作也能被前置的清单确认环节挡住。
from __future__ import annotations
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Iterable
def find_expired_files(
root: Path,
suffixes: Iterable[str],
keep_for: timedelta,
) -> list[Path]:
base = root.expanduser().resolve()
allowed = {item.lower() for item in suffixes}
deadline = datetime.now(timezone.utc) - keep_for
result: list[Path] = []
if not base.is_dir():
raise NotADirectoryError(base)
for item in base.rglob("*"):
if not item.is_file() or item.is_symlink():
continue
resolved = item.resolve()
try:
resolved.relative_to(base)
except ValueError:
continue
if item.suffix.lower() not in allowed:
continue
modified = datetime.fromtimestamp(item.stat().st_mtime, tz=timezone.utc)
if modified
rglob 会递归遍历子目录,item.is_symlink() 直接把符号链接挡在结果外。relative_to(base) 是第二道边界检查:就算工作目录里出现了指向外部的链接,也不会把外部路径带进最终候选结果。

把候选文件写成清单,确认环节不要省
清单的价值在于让「准备删除哪些文件」成为一个完全透明的可观测结果。生产环境的任务可以把它当作构建产物长期保存;本地脚本运行时至少打印出待删文件总数量、占用总大小和前几条预览路径。
def write_plan(paths: list[Path], plan_file: Path) -> None:
plan_file.parent.mkdir(parents=True, exist_ok=True)
lines = [str(path) for path in paths]
plan_file.write_text("\n".join(lines) + ("\n" if lines else ""), encoding="utf-8")
print(f"待处理文件:{len(paths)},清单:{plan_file}")
for path in paths[:5]:
print(f" - {path}")
root = Path("/srv/report-job/work")
plan = root / "cleanup-plan.txt"
targets = find_expired_files(root, {".tmp", ".part"}, timedelta(days=7))
write_plan(targets, plan)
清单文件本身不应被再次纳入待删候选,所以规则里只允许匹配 .tmp 和 .part。如果脚本要自动调度运行,建议把「候选数量超过预设阈值」设为强制失败条件,比如单次待删文件超过 5000 个就直接中断流程,避免上游任务异常导致大面积误清理。
删除阶段只消费已确认清单
确认完清单再逐条读取内容,同时重新检查对应路径仍然位于预设根目录范围内。文件很可能在等待确认的时间段内被替换,所以删除前的轻量复查环节仍然有实际意义。
def remove_from_plan(root: Path, plan_file: Path) -> tuple[int, int]:
base = root.expanduser().resolve()
removed = 0
skipped = 0
for raw in plan_file.read_text(encoding="utf-8").splitlines():
candidate = Path(raw.strip())
if not candidate.is_absolute():
skipped += 1
continue
try:
candidate.resolve().relative_to(base)
except ValueError:
skipped += 1
continue
if candidate.is_symlink() or not candidate.is_file():
skipped += 1
continue
if candidate.suffix.lower() not in {".tmp", ".part"}:
skipped += 1
continue
candidate.unlink()
removed += 1
return removed, skipped
这段代码故意没有用「清空整个目录」的简化写法。它接收的是已经生成好的路径清单,并且在每个文件上再次验证类型、位置和后缀。如果删除中途中断,剩余的清单文件仍然完整保留,可以从失败的位置继续核对处理。

定时任务里要留的检查点
脚本放进 cron、CI 或自动化任务平台后,最容易被忽略的就是异常记录。建议至少留存下面四项关键信息:
- 根目录实际解析完成后的绝对路径。
- 候选文件数量、总字节数和清单文件的存储路径。
- 成功删除数量、跳过数量以及第一个出现异常的路径。
- 清理结束后再次统计
.tmp、.part文件数量。
如果清理任务在容器中运行,还要提前确认挂载点和宿主机文件夹的对应关系。容器内的 /srv/report-job/work 可能映射到宿主机的另一个路径,日志里记录完整解析后的路径,比只写一句「清理完成」要实用得多。
常见问题:清理脚本的边界与回退
为什么不直接用 glob 加 unlink 一行写完?
简单 glob 写法适合临时一次性本地操作,但不适合长期跑的定时任务:它通常没有时间阈值、符号链接拦截和数量门禁这几层校验。先产出完整清单,能让绝大多数误删风险在真正执行删除前就暴露出来。
mtime 是文件创建时间吗?
不是。st_mtime 是内容最后修改时间,在不同文件系统和跨设备复制流程里的含义可能存在差异。如果业务要求是「上传后保留 7 天」,最好把时间字段写入数据库或者旁挂元数据,不要只依赖原生文件系统的时间属性。
清单确认后对应文件不存在怎么办?
把它标记为跳过项,不要当成成功删除记录。文件可能已经被其他并行的清理任务处理,也可能是多个同逻辑任务重试造成的资源竞争;分开统计不同状态的记录,能帮你判断后续要不要调整任务调度规则。
一份可复用的清理速查表
- 解析根目录并确认它确实是预期的工作区。
- 遍历所有子文件,跳过文件夹、符号链接和不在允许列表里的后缀。
- 按 UTC 时间戳比较 mtime,生成排序后的绝对路径清单。
- 检查待删数量和总大小,超过预设阈值就直接停止流程。
- 确认清单内容后逐条复查路径合法性,再执行删除并记录所有跳过项。
这套流程的核心不是用到了某个冷门 API,而是把「筛选候选」和「执行删除」拆成了两个带独立检查点的阶段。针对临时目录、缓存目录和批处理工作区的不同场景,都可以按相同的思路替换后缀规则、年龄阈值和数量门禁参数。
Go 1.26 的 go fix 怎么安全改造旧项目:从扫描到回归验证
- 上一篇
- Go 1.26 的 go fix 怎么安全改造旧项目:从扫描到回归验证
- 下一篇
- Go context deadline exceeded 怎么区分上游取消和下游超时:Cause 与 errors.Is 的判断顺序
-
- 文章 · python教程 | 3小时前 |
- Python subprocess 超时后子进程还在跑:用进程组和收尾顺序彻底清理
- 496浏览 收藏
-
- 文章 · python教程 | 2天前 |
- Python asyncio.gather 异常为什么会提前结束:return_exceptions 与任务取消边界
- 210浏览 收藏
-
- 文章 · python教程 | 2天前 | 并发 · 日志 · 性能 · python · Python logging QueueHandler QueueListener 并发日志
- Python 高并发日志怎么避免拖慢请求:QueueHandler、QueueListener 与退出边界
- 268浏览 收藏
-
- 文章 · python教程 | 2天前 |
- Python 百万行 CSV 怎么处理:csv 流式读取、pandas chunksize 与 SQLite 导入的取舍
- 330浏览 收藏
-
- 文章 · python教程 | 4天前 | 并发 · python · 故障排查 · asyncio · 任务取消 · Python asyncio.create_task Python 任务取消 asyncio CancelledError Python 异步任务收尾
- Python asyncio.create_task 取消后为什么还在跑:从引用丢失到任务收尾的故障复盘
- 490浏览 收藏
-
- 文章 · python教程 | 1星期前 | 字符串 · 标准库 · 模板 · python · Python 3.14 · Template Python 3.14 t-string string.templatelib PEP 750
- Python 3.14 t-string 怎么用:别把 Template 当成普通字符串
- 121浏览 收藏
-
- 文章 · python教程 | 1星期前 | [] · []
- Python Flask 表单重复提交怎么办:PRG 重定向、flash 提示和请求边界
- 343浏览 收藏
-
- 文章 · python教程 | 1星期前 | 并发编程 · python · 多线程 · asyncio · 多进程 · queue.Queue Python并发 Python任务队列 asyncio.Queue multiprocessing.Queue
- Python 任务队列怎么选:queue.Queue、asyncio.Queue 与 multiprocessing.Queue
- 165浏览 收藏
-
- 文章 · python教程 | 1星期前 | 命令行 · 异常处理 · Input · Python教程 · ValueError · 命令行交互 ValueError Python input int 输入校验 EOFError
- Python input 输入整数怎么防止 ValueError:循环校验、退出命令和 EOF 边界
- 458浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- ljg-skills
- ljg-skills 是李继刚开源的 AI 技能与提示词集合,面向大模型使用者整理了一批可复用的 prompt、角色设定和任务技能模板,适合用于学习提示词设计、搭建个人 AI 工作流和沉淀团队常用智能体能力。
- 4657次使用
-
- MELO音乐
- MELO音乐是一站式AI视频与音乐制作助手,对标suno, udio的高品质体验。提供伴奏生成、原创写词、无损导出、哼唱识曲、混音变声等全套音频与短视频编辑工具。无论是流行Kpop、电音说唱、民谣古风、摇滚儿歌还是商用轻音乐,MELO为你免费谱曲,轻松做同款!
- 4270次使用
-
- UniScribe
- UniScribe 是一款 AI 音视频转文字与内容整理工具,支持上传音频、视频文件或粘贴 YouTube 链接,自动生成转写文本、摘要、思维导图和关键问题,并支持多格式导出,适合会议记录、课程学习、访谈整理和内容创作复盘。
- 4225次使用
-
- 剧云
- 剧云是专业中文剧本创作平台,安全稳定运行十余年,集成AI编剧、剧本医生审核、人物小传、剧情关系图、大纲编写、多人协作、Word导入导出、版权管控功能,数据安全防护,轻松高效创作剧本。
- 4446次使用
-
- 万象有声
- 万象有声,一个专为有声创作者打造的新一代智能有声内容创作平台。平台提供专业的智能拆章、智能画本编辑、AI配音、AI生成音效、后期制作、智能对轨、智能审听等有声创作全流程工具,可以帮助创作者高效、低成本创作出引人入胜的有声作品。立即体验,让有声书制作更简单!
- 4405次使用
-
- go格式“占位符”输入输出 类似python的input
- 2023-01-19 346浏览
-
- Golang如何调用Python代码详解
- 2023-01-07 235浏览
-
- Go语言文件锁操作
- 2023-01-07 225浏览
-
- Go语言文件的写入、追加、读取、复制操作
- 2022-12-30 389浏览
-
- Go语言从INI配置文件中读取需要的值
- 2022-12-23 250浏览

