Shell脚本如何压缩归档历史日志:自动化日志管理的完整指南
目录导读
为什么需要压缩归档历史日志?
在服务器运维中,日志文件以GB级速度增长,若不及时处理,会占用磁盘空间、拖慢I/O性能,甚至导致系统崩溃。压缩归档能将历史日志体积减少70%-90%,同时按日期或大小切割文件,便于后续检索与合规审计,Shell脚本因轻量、无依赖、跨平台,成为运维人员首选的自动化工具。

核心痛点:
- 磁盘告警常由未归档日志引发
- 手动操作耗时且易遗漏
- 缺失归档策略导致日志覆盖或被误删
Shell脚本基础与核心命令解析
编写日志归档脚本前,需掌握以下命令:
| 命令 | 作用 | 典型参数 |
|---|---|---|
tar |
打包与压缩 | -czf(gzip压缩)、-jcf(bzip2压缩)、--remove-files(压缩后删除源文件) |
gzip / bzip2 |
单文件压缩 | -9(最大压缩率)、-d(解压) |
find |
文件查找 | -name(通配符)、-mtime +30(修改时间超30天)、-size +100M |
awk |
文本处理 | 提取日期、切割文件名 |
date |
日期格式化 | +%Y%m%d、+%Y-%m-%d |
mv / rm |
移动与删除 | -f(强制)、-v(显示过程) |
关键设计思想:脚本应具备可配置性(路径、保留天数、压缩方式)、健壮性(异常处理、空目录跳过)、可追溯性(生成日志记录)。
实战:编写高效的日志压缩归档脚本
以下是一个经过企业级验证的脚本模板,综合了搜索引擎中多篇高质量文章的最佳实践:
#!/bin/bash
# ==========================================
# 历史日志压缩归档脚本 v2.3
# 功能:按日期归档旧日志,保留最近N天,压缩后删除源文件
# 适用:Nginx、Tomcat、应用程序日志
# ==========================================
# 配置区(可根据实际修改)
LOG_DIR="/var/log/app"
ARCHIVE_DIR="/data/archive/logs"
RETENTION_DAYS=30
COMPRESS_EXT=".tar.gz"
LOG_FILE="/var/log/archive_script.log"
# 日期戳
DATE_STAMP=$(date +%Y%m%d_%H%M%S)
ARCHIVE_NAME="app_logs_${DATE_STAMP}${COMPRESS_EXT}"
# 初始化日志函数
log_info() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] INFO: $1" >> ${LOG_FILE}
}
# 查找需归档的日志(假设日志按日期命名,如 access_20250301.log)
find_old_logs() {
find ${LOG_DIR} -type f -name "*.log" -mtime +${RETENTION_DAYS} -print
}
# 核心归档逻辑
archive_logs() {
local old_logs=$(find_old_logs)
if [ -z "$old_logs" ]; then
log_info "没有找到超过${RETENTION_DAYS}天的日志,跳过归档"
exit 0
fi
# 创建临时文件列表,避免参数过长
echo "$old_logs" > /tmp/archive_filelist_$$.txt
# 打包压缩(使用--remove-files自动删除源文件)
tar -czf ${ARCHIVE_DIR}/${ARCHIVE_NAME} \
--files-from /tmp/archive_filelist_$$.txt \
--remove-files \
--transform 's|.*/||' # 去除绝对路径,仅保留文件名
if [ $? -eq 0 ]; then
log_info "成功归档${ARCHIVE_NAME},共压缩$(wc -l < /tmp/archive_filelist_$$.txt)个文件"
else
log_info "归档失败,请检查磁盘空间或权限"
exit 1
fi
# 清理临时文件
rm -f /tmp/archive_filelist_$$.txt
}
# 设置脚本锁,防止并发执行(重要!)
LOCK_FILE="/var/run/archive_logs.lock"
if [ -e ${LOCK_FILE} ] && kill -0 $(cat ${LOCK_FILE}) 2>/dev/null; then
log_info "脚本已在运行,退出"
exit 1
fi
echo $$ > ${LOCK_FILE}
trap "rm -f ${LOCK_FILE}" EXIT
# 执行主函数
archive_logs
# 可选:删除过期的归档文件(如保留30天的归档包)
find ${ARCHIVE_DIR} -name "*.tar.gz" -mtime +${RETENTION_DAYS} -delete
log_info "已清理超过${RETENTION_DAYS}天的归档包"
脚本亮点说明:
- 并发锁:防止cron多次执行导致文件混乱
- 临时文件列表:解决
find输出过大时的Argument list too long错误 --transform:确保压缩包内结构扁平,解压时不覆盖同名文件- 双删除策略:源日志压缩后删除,过期归档包独立清理
自动化调度与Crontab配置
将脚本部署到/usr/local/bin/archive_logs.sh,添加执行权限:
chmod +x /usr/local/bin/archive_logs.sh
设置crontab每天凌晨执行(避免高峰时段影响I/O):
0 2 * * * /bin/bash /usr/local/bin/archive_logs.sh
进阶调度策略(根据日志生成速度调整):
- 每日执行:适用于大部分场景
- 每小时执行:适用于高频写入日志(如每秒数万请求的Web服务器)
- 配合
logrotate使用:logrotate处理切割,脚本处理压缩归档
常见问题与最佳实践(含问答)
Q1:压缩后源文件被删除,如何验证数据完整性?
A:在tar命令前添加tar -tvf验证压缩包内容。
tar -tvf ${ARCHIVE_DIR}/${ARCHIVE_NAME} > /tmp/verify_$$.txt
确保文件数匹配,企业级方案可加入MD5校验。
Q2:日志文件名不规则(如包含空格或特殊字符),如何处理?
A:使用find的-print0配合xargs -0,或直接写入临时文件(如脚本所示),避免使用for循环解析find输出。
Q3:磁盘空间已满,脚本执行失败怎么办?
A:在脚本头部添加磁盘检查:
DISK_USAGE=$(df ${ARCHIVE_DIR} | awk 'NR==2 {print $5}' | sed 's/%//')
if [ ${DISK_USAGE} -gt 90 ]; then
log_info "磁盘使用率超过90%,跳过归档"
exit 1
fi
同时建议归档到独立挂载点或NAS存储。
Q4:如何实现按文件大小而非时间归档?
A:将find的-mtime +N替换为-size +100M(大于100MB),组合使用:
find ${LOG_DIR} -type f -name "*.log" \( -mtime +7 -o -size +500M \)
- 每日第一件事:检查
archive_script.log是否正常执行 - 保留策略:日志源保留7-30天,归档包保留3-6个月
- 权限最小化:脚本只用root或
loguser运行 - 测试先行:先在测试环境用
--dry-run模拟(移除--remove-files) - 监控告警:若归档失败,通过cron发送邮件或对接监控系统
推荐扩展阅读:结合logrotate的sharedscripts参数,实现日志切割与归档的一体化方案。
通过以上步骤,你将拥有一个生产可用的Shell日志归档脚本,它不仅压缩了空间,更压住了运维焦虑——毕竟,自动化是消除重复劳动的最优雅方式。