Linux 定时任务手动执行正常,cron 却不运行?环境变量、路径与日志排查

脚本手动执行正常,加入 crontab 却没有结果,需要区分未调度、启动失败和运行失败。本文从任务账号、用户与系统任务格式、日志证据、PATH、工作目录、百分号和时区逐步排查,并给出包装脚本、精简环境重现与恢复验收方法,避免盲目提升到 root 或重复补跑造成业务风险。
Linux 定时任务手动执行正常,cron 却不运行?环境变量、路径与日志排查

脚本在 SSH 会话中执行成功,加入 crontab 后却没有生成报表,也没有备份文件。此时需要区分两件事:调度器有没有启动命令,以及命令启动后有没有完成业务。没有产物,并不能直接证明 cron 没触发。

最常见的差异是运行账号、PATH、工作目录和解释器。交互式终端已经加载的虚拟环境、别名和登录配置,不会自动成为 cron 的执行环境。排查应从调度记录与任务输出入手,不宜直接把任务改成 root 执行。

本文适用于 Linux 上常见的 Cronie 或传统 cron 实现。不同发行版的服务名称、日志位置、默认环境和时区支持存在差异,具体以本机手册为准。示例账号为 appuser,程序位于 /srv/report,请替换后使用。systemd timer、容器内调度、Kubernetes CronJob 和 WordPress WP-Cron 不属于同一种调度机制。

确认任务属于哪个账号、使用哪种格式

先查看任务实际安装位置,而非只看编辑过的文件:

crontab -l
sudo crontab -u appuser -l

第一条查看当前账号,第二条查看指定账号,需要相应权限。登录为 root 后执行 crontab -e,修改的是 root 的用户任务,不能据此判断 appuser 的任务已更新。

用户 crontab 的基本格式是五个时间字段加命令;/etc/crontab 和常见 /etc/cron.d/ 文件通常还需要一个运行用户字段:[1]

# 用户 crontab:每小时第 15 分钟运行
15 * * * * /srv/report/run-report.sh

# 系统 crontab 或 /etc/cron.d/:多一个用户字段
15 * * * * appuser /srv/report/run-report.sh

这两行用于展示格式差异,不应同时安装。把系统格式复制进用户 crontab,会把用户名当作命令的一部分执行;反过来则可能被判为无效配置。

修改前备份已有任务,并保留原文件权限与内容。可使用 crontab -l 将已有条目导出到受保护的位置,再通过 crontab -e 编辑。不要用只含一条新任务的文件覆盖整个任务表,也不要运行 crontab -r 来“重置”。通过 crontab 工具安装的变更通常无需重启守护进程。[2]

先找调度证据,再找命令输出

常见服务名是 cron 或 crond。以下两组命令按发行版选择:

systemctl status cron --no-pager
journalctl -u cron --since '1 hour ago' --no-pager

systemctl status crond --no-pager
journalctl -u crond --since '1 hour ago' --no-pager

日志也可能进入 /var/log/cron、syslog 或其他日志系统;journal 中没有记录并不一定代表未执行。检查本机 syslog 路由、日志权限及服务状态。系统对 cron 的访问控制、账号过期或 PAM 限制也可能阻止任务运行。

日志中的 CMD 通常只能证明调度器尝试启动命令。任务退出码、标准错误和业务产物仍需单独记录。传统 cron 可以把输出交给本机邮件机制,但主机没有配置邮件投递时,不能指望邮箱收到错误报告。

在测试环境可添加一次短期、无副作用的调度探针,把时间写入预先创建、只允许任务账号写入的日志目录。测试结束及时撤销,避免与正式任务混淆。不应把数据库导入、扣费或通知发送改成每分钟执行来观察故障。

将解释器、路径和工作目录写清楚

下面是一份包装脚本示例,它依赖系统存在 /bin/sh、date 和指定 Python。日志目录需提前按站点权限模型创建,不要在排错时设为全局可写:

#!/bin/sh
PATH=/usr/bin:/bin
export PATH
umask 027
cd /srv/report || exit 1
printf 'start %s\n' "$(date -Is)"
/srv/report/.venv/bin/python /srv/report/report.py
rc=$?
printf 'finish %s exit=%s\n' "$(date -Is)" "$rc"
exit "$rc"

在用户 crontab 中调用:

15 * * * * /bin/sh /srv/report/run-report.sh >> /srv/report-log/cron.log 2>&1

这里假定日志目录为 /srv/report-log,且 appuser 可以创建或追加该日志。Shell 会先处理重定向;日志路径不可写时,脚本甚至不会开始执行。日志应配置轮转、保留周期与访问限制,避免长期积累或泄露业务数据。

这个例子直接调用虚拟环境里的 Python,无需依赖交互式 source activate。如果脚本使用 Bash 语法,应明确调用 Bash,不能继续用 /bin/sh。直接执行脚本时还要检查 shebang、执行权限和 Windows CRLF 换行;通过解释器调用则仍需脚本可读和父目录可访问。

cron 通常用配置的 SHELL 执行命令,不能假定它会加载 .bashrc、Shell 别名或交互式终端中的 PATH。[1] 所需环境变量应由受控配置或程序自身机制提供,含密码的值不要写进公开任务表、命令行或诊断输出。

百分号、时间与系统任务目录容易被忽略

在常见 crontab 命令字段中,未转义的 % 会被特殊处理,后面的内容可能转为标准输入。[1] 因此,将 date +%F 原样写在任务行中,可能与终端执行结果不同。可按本机实现使用 \%,或把带日期格式的命令放入包装脚本。该特殊处理发生在 cron 命令字段,脚本文件内部的百分号无需因此转义。

检查系统时间和预期时区:

date -Is
timedatectl status

服务器使用 UTC,而维护人员按北京时间理解“凌晨两点”,会造成误判。CRON_TZ 并非所有实现都支持;支持时,调度时区和日志显示时区也可能不同。夏令时与时钟跳变的处理存在实现差异,重要任务应核对当地时间策略及本机手册。[1][2]

普通定时条目通常不会在关机期间运行,也不应默认认为开机后自动补跑。发行版可能额外使用 anacron 或 systemd timer,必须确认实际入口,避免为“补跑”再加一条重复任务。

/etc/cron.daily 等目录常由 run-parts 或发行版机制调用,脚本名称、执行权限和执行时段有额外规则;放进去一个文件并不代表下一分钟就会运行。/etc/cron.d/ 同样有属主、权限、文件名及格式要求,不能把用户任务表直接复制过去。

用相同账号和精简环境重现

如果调度已发生,下一步应逼近 cron 环境,而非继续在自己的登录终端运行。以下命令会真实执行任务,只有确认任务可重复、无危险副作用或已准备测试模式后才运行:

sudo -u appuser env -i \
  HOME=/home/appuser USER=appuser LOGNAME=appuser \
  PATH=/usr/bin:/bin SHELL=/bin/sh \
  /bin/sh -c 'cd / && /bin/sh /srv/report/run-report.sh'

HOME 应替换为该账号的真实主目录。这个命令用于暴露路径和环境依赖,不会完整复刻 cron 的 PAM、SELinux 上下文或服务隔离。若精简环境失败而交互终端成功,重点检查缺失变量、相对路径、配置文件与程序版本。

若仍有差异,检查账号权限、网络访问、DNS、挂载状态及安全审计记录。不要为验证猜想关闭 SELinux 或把脚本改成 root 运行;应针对明确拒绝原因修复策略或配置。

检查是否启动了,却卡住或重复执行

任务超过一个周期仍未结束,新一轮可能继续启动。检查进程、程序日志、数据库连接和已有锁。不要看到“没有新产物”就不断手动补跑,这可能生成重复账单、重复备份或并发写入。

对不允许并发的任务,可评估使用应用锁或 flock,但应明确锁的位置、生命周期与竞争时的退出行为。网络文件系统上的锁语义需要额外确认。为任务加超时也需评估进程终止后的事务与临时文件清理,不能把强制结束当成通用恢复。

验收至少覆盖一次真实调度:记录开始与结束时间、退出码、产物位置及业务有效性,并确认下一个周期没有重叠。退出码为 0 仍可能只是脚本吞掉了错误;备份任务还需验证文件可读取、内容正确并纳入恢复演练。

如果本次修改未解决问题,可恢复原任务表与包装脚本,核对期间是否遗漏或重复执行,再决定是否补跑。涉及发信、扣费、导入等操作时,应先检查业务幂等性。最终要能区分“未触发”“启动失败”“运行失败”和“完成但结果不对”,后续告警也应分别覆盖这些状态。

参考资料

资料核验日期:2026 年 10 月 26 日。命令为说明性示例,本文未声称已在读者生产环境实测。

  1. crontab(5):任务格式、环境变量、百分号与时间规则
  2. cron(8):调度守护进程与实现行为
Linux运维

SSH 提示主机密钥已更改怎么办?重装后的指纹核验与安全恢复连接

2026-10-22 10:44:46

主机测评实操指南知识库

向量搜索服务器性能基准测试

2024-12-6 15:49:54