3. 定时清理过期临时文件

定时清理过期临时文件 学习卡片

任务拆解:保留期限、扫描范围与删除清单

本案例处理的对象是“指定目录中的临时文件”,核心约束是“超过保留期限”。脚本需要完成三件事:按保留天数筛选过期文件、删除这些文件、把删除结果写入清单日志。与批量重命名或分类归档不同,这里的判断依据是文件的修改时间(mtime),而不是文件名或扩展名。

关键参数定义如下:

  • target_dir:要清理的目录,例如 /tmp/myapp_cache。
  • retention_days:保留期限,单位为天,例如 7 天。
  • log_file:删除清单日志路径,例如 cleanup_log.txt。
  • now:当前时间戳,用于计算过期阈值。

过期判断公式为:now - file.stat().st_mtime > retention_days * 86400。其中 86400 是一天的秒数。只有严格超过保留期限的文件才会被删除,等于阈值时保留。

建议先用“只记录不删除”的 dry-run 模式跑一遍,确认清单无误后再开启真实删除。这样能避免误删仍在使用的临时文件。

核心实现:扫描、判断、删除、记录

下面给出一个完整可运行的脚本。它使用 pathlib 遍历目录,用 time.time() 获取当前时间,用 datetime 生成可读时间戳,并把每个被删除文件的路径、修改时间和文件大小写入日志。

from pathlib import Path
from datetime import datetime
import time

def cleanup_expired_files(target_dir, retention_days, log_file, dry_run=True):
    target = Path(target_dir)
    if not target.is_dir():
        raise NotADirectoryError(f"目录不存在: {target_dir}")

    now = time.time()
    threshold = retention_days * 86400
    deleted = []

    for item in target.rglob("*"):
        if not item.is_file():
            continue
        try:
            stat = item.stat()
        except OSError as e:
            print(f"跳过无法访问的文件: {item} ({e})")
            continue

        age = now - stat.st_mtime
        if age > threshold:
            mtime_str = datetime.fromtimestamp(stat.st_mtime).strftime("%Y-%m-%d %H:%M:%S")
            record = f"{item}\t{mtime_str}\t{stat.st_size} bytes"
            deleted.append(record)
            if not dry_run:
                try:
                    item.unlink()
                except OSError as e:
                    print(f"删除失败: {item} ({e})")
                    deleted.pop()
                    continue

    with open(log_file, "a", encoding="utf-8") as f:
        f.write(f"# 清理时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
        f.write(f"# 目录: {target_dir}  保留天数: {retention_days}  模式: {'dry-run' if dry_run else 'delete'}\n")
        for line in deleted:
            f.write(line + "\n")
        f.write(f"# 共处理 {len(deleted)} 个文件\n\n")

    return deleted

if __name__ == "__main__":
    result = cleanup_expired_files(
        target_dir="/tmp/myapp_cache",
        retention_days=7,
        log_file="cleanup_log.txt",
        dry_run=True
    )
    print(f"本次{'预演' if True else '实际'}清理 {len(result)} 个文件")
    for r in result:
        print(r)

脚本中几个容易出错的点:rglob("*") 会递归扫描子目录,如果只想清理顶层文件,应改用 iterdir();item.is_file() 过滤掉目录,避免对目录调用 unlink();dry_run=True 时只记录不删除,适合首次验证。

删除清单日志格式与判读

日志采用追加写入,每次运行生成一个块,块内包含运行参数和每个被处理文件的记录。这样即使多次运行,也能追溯每次清理的范围和结果。

字段示例说明
文件路径/tmp/myapp_cache/session_old.tmp被判定为过期的文件完整路径
修改时间2025-01-10 08:30:00文件最后修改时间,用于核对是否真的过期
文件大小2048 bytes便于评估清理释放的空间
运行参数目录、保留天数、模式记录本次清理的上下文

判读日志时,重点看三件事:被删除文件是否确实超过保留期限;是否有“删除失败”的记录;dry-run 和真实删除两次运行的清单是否一致。如果 dry-run 清单为空,说明没有文件超过保留期限,不需要执行删除。

定时执行与安全边界

“定期删除”通常借助系统定时任务实现。Linux 下可用 cron,Windows 下可用任务计划程序。以 cron 为例,每天凌晨 3 点执行一次:

0 3 * * * /usr/bin/python3 /opt/scripts/cleanup_expired_files.py >> /var/log/cleanup_cron.log 2>&1

安全边界建议:

  • 不要对系统关键目录(如 /etc、/usr)执行清理。
  • 保留期限不要设得过短,避免误删仍在使用的缓存。
  • 日志文件本身不要放在被清理目录内,否则可能被自己删除。
  • 首次上线先用 dry-run 运行至少一个周期,确认清单稳定后再切换为真实删除。
如果目标目录中存在正在被其他进程写入的临时文件,删除可能导致对方写入失败。建议在业务低峰期执行,或先与相关服务确认文件生命周期。
定时清理过期临时文件:扫描—判断—删除—记录 输入参数 target_dir = /tmp/myapp_cache retention_days = 7 log_file = cleanup_log.txt 扫描目录 rglob("*") 递归遍历 is_file() 过滤目录 stat() 读取 mtime/size 过期判断 age = now - st_mtime threshold = retention_days × 86400 age > threshold → 过期 dry_run = True 只记录,不调用 unlink() 用于首次验证清单 输出:预演清单 dry_run = False 调用 item.unlink() 删除 删除失败则回滚记录 输出:实际删除清单 写入删除清单日志 路径 + 修改时间 + 文件大小 追加模式,每次运行一个块 cron 定时触发 0 3 * * *

验证与常见问题

验证脚本是否按预期工作,可以手动构造测试文件:

mkdir -p /tmp/myapp_cache
touch -d "10 days ago" /tmp/myapp_cache/old.tmp
touch /tmp/myapp_cache/new.tmp
python3 cleanup_expired_files.py

预期结果:old.tmp 出现在清单中,new.tmp 不出现。如果 dry-run 模式下清单为空,检查 retention_days 是否过大,或文件 mtime 是否被正确读取。

现象可能原因处理方式
清单为空没有文件超过保留期限确认 retention_days 和文件 mtime
删除失败文件被占用或权限不足检查进程占用和目录权限
日志文件也被删日志放在被清理目录内把 log_file 移到目录外
子目录文件未被清理使用了 iterdir() 而非 rglob()按需改为递归扫描
核心记忆点:过期判断看 mtime,删除前先 dry-run,删除结果写清单,定时执行用 cron。这四步构成一个可追溯、可回滚的临时文件清理闭环。