crontab -l 显示为空但任务仍在执行的系统级 cron.d 排查

10次阅读

crontab -l 看不到 /etc/cron.d/ 下的任务,因其仅显示当前用户级 crontab,而 cron.d 文件由 crond 进程独立加载,需检查日志、权限、命名及语法。

crontab -l 显示为空但任务仍在执行的系统级 cron.d 排查

crontab -l 为什么看不到 cron.d 下的任务

crontab -l 只显示当前用户的用户级 crontab(即 crontab -e 编辑的文件),完全不读取系统级目录 /etc/cron.d/ 中的任务。这是设计如此,不是 bug —— 系统级任务和用户级任务走的是两套加载机制。

系统级 cron.d 文件由 crond 进程在启动时或收到 SIGHUP 时扫描加载,路径固定为:/etc/cron.d/ 下所有非隐藏、可读、权限合规(不能被组/其他写)的文件。

  • 文件名不能含点号(.)或波浪线(~),否则会被忽略
  • 必须有明确的用户名字段(第6列),例如 * * * * * root /path/to/script.sh
  • 不支持 @reboot@daily 等特殊时间语法,只接受标准五段时间格式

如何确认 /etc/cron.d/ 下的任务是否被 crond 加载

直接查 crond 的日志最可靠。多数发行版默认将 cron 日志发到 /var/log/cron(RHEL/centos)或 /var/log/syslogdebian/ubuntu)。用 grep 搜索 cron.d 目录相关线索:

sudo grep "cron.d" /var/log/cron 2>/dev/NULL || sudo grep "CRON" /var/log/syslog | grep -i "cron.d"

如果看到类似 READ /etc/cron.d/myjobLOAD /etc/cron.d/myjob 的记录,说明已加载;若无,则可能是权限、命名或语法问题。

  • 检查文件权限:ls -l /etc/cron.d/myjob —— 必须是 root:root,且不能有 group/others 的写权限(即 0644 合理,0664 会被拒绝)
  • 检查文件末尾是否有空行:某些老版本 crond(如 v8.1.5 之前)会因最后一行为空而跳过整个文件
  • 确认时间字段后紧跟着用户名,中间不能有多余空格或制表符混用(建议全用空格)

任务在执行但没日志?快速验证是否真由 cron.d 触发

在脚本开头加一行日志输出,绕过 syslog,直写文件:

echo "$(date): started by $(ps -o comm= $PPID)" >> /tmp/myjob.debug 2>&1

这样能区分是 cron.d 调用、手动运行,还是其他进程(比如 systemd timer 或另一个 crontab)触发的。

  • 如果 $PPID 对应的 commcrond,基本锁定是 cron.d 或系统 crontab 执行的
  • 如果看到 bashsh,再往上查 ps -o pid,ppid,comm= $PPID,可能父进程是某个 wrapper 脚本
  • 注意:有些任务会重定向 stdout/stderr 到 /dev/null,导致你以为“没输出”,其实执行了

常见静默失败原因:语法合法但不生效

即使 crond 加载了文件,任务也可能因环境差异“看似没执行”。最典型的是 PATH 和 SHELL 不同:

  • 系统级 cron.d 默认使用 /bin/sh,不是 /bin/bash;如果脚本里用了 [[source,可能直接报错退出
  • 默认 PATH=/usr/bin:/bin,不含 /usr/local/bin 或用户 $HOME/bin;命令未带绝对路径就容易找不到
  • 没有继承 shell 的环境变量(如 $HOME$USER),~ 在脚本中不会展开,cd ~ 会失败
  • 某些发行版(如 Ubuntu)的 cron.d 会额外校验文件 mtime 是否早于 crond 启动时间,修改后需 sudo killall -HUP crond 或重启服务

真正难排查的,往往不是“没加载”,而是“加载了但执行失败又没留痕”。加绝对路径、显式指定 /bin/bash、重定向完整 stderr,比反复看 crontab -l 有用得多。

text=ZqhQzanResources