cron触发的月末shell脚本在部分月份最后一天无法运行排查
故障潜在根因
- crontab触发规则完全不符合预期
当前配置的5 0 * * *规则是每天凌晨0点05分全量执行脚本,根本没有做「仅每月最后一天执行」的限制。非月末日期运行时,脚本内的月末文件挂载、过期清理逻辑匹配不到对应日期的文件,会直接触发路径不存在、误操作等问题。 - 日期计算存在经典溢出bug
脚本中EXPIREDDATE=date +%Y%m -d "last month"``的写法有严重逻辑缺陷:GNU date对last month的计算逻辑是直接将月份数值减1,不会自动校正日期合法性。如果运行当天是31号,而前一个月总天数不足31天(4/6/9/11月共30天,平年2月28天、闰年2月29天),溢出的日期会自动顺延到当前月,最终返回的EXPIREDDATE是当前月份而非上月。比如5月31日执行该命令会得到5月的结果,直接导致过期清理逻辑误删当前正在使用的文件、路径拼接错误。 - 日期参数存在兼容性问题
脚本中date +%-d的%-d是GNU date专属格式符,作用是去掉日期前导0。如果运行环境是BSD系统、BusyBox精简环境或者未使用GNU coreutils,该参数会直接报错,导致DATENOW变量赋值为空,依赖该变量的上月文件保留期判断逻辑直接崩溃。 - cron运行环境与登录环境不一致
cron执行任务时不会加载用户登录后的环境变量,默认PATH仅包含/usr/bin:/bin等基础路径,如果date、mount等命令不在默认路径下,会直接报命令不存在的错误;如果脚本未配置可执行权限、cron执行时的工作目录不符合预期,也会触发各类路径相关报错。
修复方案
- 修正定时触发规则,实现真正的每月最后一天执行
调整crontab配置,仅在每月可能是最后一天的28-31号触发脚本,再在脚本入口增加日期校验,非月末直接退出:
在脚本shebang行之后增加如下校验逻辑:5 0 28-31 * * /root/mount_monthly_file.sh# 非当月最后一天直接退出 if [ "$(date +%d -d tomorrow)" != "01" ]; then exit 0 fi - 替换有溢出风险的日期计算逻辑
统一用「当月首日倒推」的方式计算跨月日期,彻底解决日期溢出问题,同时替换GNU专属的格式符提升兼容性:# 提前计算当月首日,复用减少重复调用 CURRENT_MONTH_FIRST=$(date +%Y%m01) SERVICEDATE=$(date +%Y%m) # 用上月最后一天的年月作为过期月份,无溢出风险 EXPIREDDATE=$(date +%Y%m -d "$CURRENT_MONTH_FIRST -1 day") SOURCEPATH="/mnt/aws_sg01/refdata/1/refdata/lse/" SERVICEPATH="/srv/latest/refdata/lse/" # 用sed替换去掉前导0,兼容所有date实现 DATENOW=$(date +%d | sed 's/^0//') PREVMONTHLASTDAY=$(date +%Y%m%d -d "$CURRENT_MONTH_FIRST -1 day") PREVMONTHRETENTION=3 # 上月最后一天的文件在当月保留3天 - 补齐cron运行环境配置
在脚本开头主动声明PATH,覆盖所有常用命令路径,避免环境差异导致的命令找不到问题:
提前给脚本加可执行权限:#!/bin/bash export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"chmod +x /root/mount_monthly_file.sh
内容的提问来源于stack exchange,提问作者Jake Zywiol
相关产品推荐
相关产品推荐

