如何编写磁盘清理脚本

wen 实用脚本 1

从入门到精通的自动化运维指南

目录导读

  1. 为什么需要磁盘清理脚本 – 磁盘告警的痛点与自动化需求
  2. 清理脚本的核心设计原则 – 安全、可追溯、可配置
  3. 基础清理命令解析rmfinddudf 的实战组合
  4. 进阶技巧:按时间/大小/类型精准筛选 – 摆脱“一刀切”删除
  5. 日志与安全机制 – 防止误删的五大防护策略
  6. 完整脚本实例与逐行解读 – 可直接部署的生产级代码
  7. 计划任务与监控集成 – Crontab、Systemd Timer 与告警联动
  8. 常见问题答疑(FAQ) – 针对高频率疑问的精准回答

为什么需要磁盘清理脚本?

运维人员的日常中,最让人心跳加速的告警之一就是:Disk space has reached 90%,手动清理不仅耗时,且容易误操作,编写一个磁盘清理脚本的核心目的有三:

如何编写磁盘清理脚本

  • 自动化:在无人工干预下,定时释放磁盘空间。
  • 精准化:只删除“过期”或“临时”文件,绝不触碰系统关键数据。
  • 可审计:每次清理行为留有日志,方便追责与回滚。

根据统计,合理的清理脚本能减少 70% 的磁盘满告警事件,并且大幅缩短故障恢复时间。


清理脚本的核心设计原则

在动手写代码前,必须明确四个原则:

  1. 白名单机制:明确“只删什么”,而非“除了什么不删”。
  2. 干跑模式(Dry-run):正式执行前,必须先输出“将要删除的文件列表”,人工确认。
  3. 时间阈值可选:如 -30 代表删除30天前的文件,但必须支持参数传入。
  4. 错误处理:磁盘空间不足时容易引发命令中断,需捕获错误码并发送告警。

基础清理命令解析

下面是最常用的命令组合,建议先掌握再写脚本:

命令 用途 关键参数
df -h 查看磁盘分区使用率 -h 人性化显示
du -sh * 统计目录大小 -s 汇总,-h 可读
find 按条件查找文件 -mtime-size-type
rm -rf 强制删除 慎用,需与 find 配合

基础组合示例(删除 /tmp 下7天前的 .log 文件):

find /tmp -type f -name "*.log" -mtime +7 -exec rm -f {} \;

进阶技巧:按时间/大小/类型精准筛选

1 按时间维度

-mtime(修改时间)与 -atime(访问时间)的选择很重要:

  • 备份文件用 -mtime +30(30天前修改过)。
  • 缓存文件用 -atime +7(7天前未被访问)。

2 按大小维度

find /var/log -type f -size +100M -delete

> 会删除大于100MB的任何文件,需谨慎确认。

3 按类型与目录排除

find /home -type f \( -name "*.tmp" -o -name "*.cache" \) -not -path "/home/public/*" -delete

支持逻辑运算符与排除目录,实战中几乎必用。


日志与安全机制

误删是磁盘清理脚本最大的风险,以下策略必须至少实现3条:

  1. 重建回收站:删除前将文件 mv/tmp/recycle_$(date +%F) 目录,保留7天。
  2. 检查变量非空:务必判断目录变量是否为空,防止 rm -rf /
  3. 软链接防护:用 -type f 限制,避免删除链接目标。
  4. 并发锁:防止脚本被重复执行,使用 flockmkdir 锁文件。
  5. 执行前备份列表find ... > /var/log/clean_list_$(date +%F).txt,便于恢复。

完整脚本实例与逐行解读

以下是一个生产环境级脚本,请按需修改路径与阈值:

#!/bin/bash
# =============================================
# Author: OpsMaster
# Description: Safe disk cleanup with dry-run & logging
# Usage: ./disk_cleaner.sh [--dry-run] [--days=30] [--dir=/var/tmp]
# =============================================
# 初始化变量
DRY_RUN=false
DAYS=30
TARGET_DIR="/var/tmp"
LOG_FILE="/var/log/disk_cleaner.log"
RECYCLE_BIN="/tmp/recycle_$(date +%F)"
# 参数解析
while [[ "$#" -gt 0 ]]; do
  case $1 in
    --dry-run) DRY_RUN=true ;;
    --days) DAYS="$2"; shift ;;
    --dir) TARGET_DIR="$2"; shift ;;
    *) echo "Unknown option: $1"; exit 1 ;;
  esac
  shift
done
# 安全检测
if [ -z "$TARGET_DIR" ] || [ ! -d "$TARGET_DIR" ]; then
  echo "ERROR: Invalid target directory." | tee -a "$LOG_FILE"
  exit 1
fi
# 日志函数
log_info() {
  echo "[$(date '+%Y-%m-%d %H:%M:%S')] INFO: $1" | tee -a "$LOG_FILE"
}
# 干跑与正式执行
if $DRY_RUN; then
  log_info "DRY-RUN mode. Files to be deleted (older than ${DAYS} days):"
  find "$TARGET_DIR" -type f -mtime +"$DAYS" -name "*.tmp" -o -name "*.log" -print
else
  log_info "Starting cleanup for ${TARGET_DIR} (older than ${DAYS} days)"
  mkdir -p "$RECYCLE_BIN"
  # 移动到回收站而非直接删除
  find "$TARGET_DIR" -type f \( -name "*.tmp" -o -name "*.log" \) -mtime +"$DAYS" \
    -exec mv {} "$RECYCLE_BIN/" \; -exec touch "$RECYCLE_BIN/$(basename {}).moved" \;
  log_info "Moved files to ${RECYCLE_BIN}"
fi
# 磁盘空间报告
df -h | tee -a "$LOG_FILE"

逐行深度解析(关键四行):

  • find ... -exec mv {} "$RECYCLE_BIN/" – 安全删除,非 rm
  • touch ... .moved – 记录原路径的痕迹,便于回溯。
  • $DRY_RUN 分支 – 先打印结果,避免盲目删除。
  • tee -a – 日志双写(控制台+文件)。

计划任务与监控集成

1 Crontab 定时执行

# 每天凌晨2点执行,并输出日志
0 2 * * * /usr/local/bin/disk_cleaner.sh --days=30 --dir=/var/tmp >> /var/log/disk_cleaner_cron.log 2>&1

2 Systemd Timer(推荐)

创建 cleaner.service(执行命令)和 cleaner.timer(定时触发):

# /etc/systemd/system/disk-clean.timer
[Unit]
Description=Run disk cleaner daily
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target

3 与监控告警联动

在脚本开头判断 df -h / | awk 'NR==2 {print $5}' 的百分比,若超过90%,直接通过 curl 发送webhook告警到钉钉/企业微信。


常见问题答疑(FAQ)

Q1:脚本误删了重要文件,可以恢复吗? A: 如果你使用了上述“回收站”机制(mv 而非 rm),可以进入 /tmp/recycle_$(date) 恢复,否则,建议立刻停止写入磁盘,并使用 extundelete 尝试恢复(不保证100%成功)。

Q2:清理日志文件后,服务报错“无法写入日志”,怎么办? A: 这是常见坑,原因通常是删除后未重开文件描述符,推荐用 logrotate 而非纯 find+rm,它能在清理后执行 postrotate 脚本(如 systemctl reload rsyslog)。

Q3:为什么我的 find 命令在云服务器上很慢? A: 云服务器磁盘(常用SDD)随机读取查询慢,优化技巧:find 指定目录层级 -maxdepth 2;优先使用 -name 匹配;避免遍历整个 。

Q4:能否递归清理 Docker 的残留日志? A: 可以,Docker容器日志默认在 /var/lib/docker/containers/*/*-json.log,加上 -name "*.log" 并配合 truncate -s 0(清空而非删除,防止容器句柄丢失)即可。

Q5:如何判断脚本是否执行成功? A: 检查 退出码(0为成功),同时查看日志末尾是否有 ERROR 关键词,建议在脚本结尾加上 if [ $? -eq 0 ]; then exit 0; else exit 1; fi


延伸阅读: 若想进一步优化,可研究 tmpwatch 工具(针对 /tmp 的专属清理),或者利用 ZFS/Btrfs 文件系统的快照功能,实现秒级“时间点”回滚。


希望这篇磁盘清理脚本编写指南能让你告别“磁盘告警综合症”,务必先在小规模环境测试至少一周,再部署到生产。充分验证过的脚本,才是好脚本。

抱歉,评论功能暂时关闭!