【转载】记录一次Mac上crontab未成功执行问题的排查过程!
原文地址 blog.humh.cn
记录一次排查Mac上编写的crontab任务无法成功执行的过程!
问题
当前登录用户下,crontab -e编写的任务竟然没有成功执行(因为逻辑中加入了,任务只要运行,就会输出日志,显然没有对应输出)
排查
1、跟踪crontab自身日志
crontab日志默认输出“/var/log/cron.log”中。
发现没有最新cron输出,怀疑crontab任务并没有运行?
2、检查crontab任务是否正常运行
如同Ubuntu中的systemctl一样,Mac也提供了服务管理的工具launchctl,和systemctl一样都是作为系统超级进程,管理其他服务。这里需要知道,launchctl是通过plist文件进行服务管理的,每个plist都是一个服务。在整个OS中有多个目录分类存放不同的plist文件,不同目录对应不同的服务类型。如下:
- ~/Library/LaunchAgents 由用户自己定义的服务项
- /Library/LaunchAgents 由管理员为用户定义的服务项
- /System/Library/LaunchAgents 由Mac OS 为用户定义的服务项
- /Library/LaunchDaemons 由管理员定义的守护进程服务项
- /System/Library/LaunchDaemons 由Mac OS 定义的守护进程服务项
“/System/Library” 、“ /Library”、“~/Library”目录的区别在于 /System/Library目录是存放Apple自己开发的软件。 /Library目录是系统管理员存放的第三方软件。 ~/Library/是用户自己存放的第三方软件。 而LaunchDaemons和LaunchAgents的区别在于 LaunchDaemons是用户未登陆前就启动的服务(守护进程)。 LaunchAgents是用户登陆后启动的服务(守护进程)。所以一般我们自定义的plist一般写在“~/Library/LaunchAgents”下。
关于launchctl的内容,这里就不过多阐述了,可以自行了解。
有了launchctl的基本了解,接下来排查crontab服务的运行。
首先,launchctl list | grep 'cron'
查看是否存在crontab服务,应该没有,再sudo launchctl list | grep 'cron'
,这是出现crontab服务
Plain textCopy to clipboardOpen code in new windowEnlighterJS 3 Syntax Highlighter humh@MacBook-Pro ~ launchctl list | grep 'cron' ✘ humh@MacBook-Pro ~ sudo launchctl list | grep 'cron'201 0 com.vix.cron humh@MacBook-Pro ~ launchctl list | grep 'cron' ✘ humh@MacBook-Pro ~ sudo launchctl list | grep 'cron' 201 0 com.vix.cron
humh@MacBook-Pro ~ launchctl list | grep 'cron'
✘ humh@MacBook-Pro ~ sudo launchctl list | grep 'cron'
201 0 com.vix.cron
其中第一列是pid;第二列是服务状态,0代表正常;第三列是服务plist名。之所以sudo查看有,是因为crontab默认Mac系统启动服务。
那么问题来了,既然服务正常,为什么crontab看起来没有执行,于是看了下plist,locate com.vix.cron
或全局查找plist文件位置。
Plain textCopy to clipboardOpen code in new windowEnlighterJS 3 Syntax Highlighter humh@MacBook-Pro ~ locate com.vix.cron/System/Library/LaunchDaemons/com.vix.cron.plist humh@MacBook-Pro ~ locate com.vix.cron /System/Library/LaunchDaemons/com.vix.cron.plist
humh@MacBook-Pro ~ locate com.vix.cron
/System/Library/LaunchDaemons/com.vix.cron.plist
plist大致内容如下
Plain textCopy to clipboardOpen code in new windowEnlighterJS 3 Syntax Highlighter
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.vix.cron</string>
<key>ProgramArguments</key>
<array>
<string>/usr/sbin/cron</string>
</array>
<key>KeepAlive</key>
<dict>
<key>PathState</key>
<dict>
<key>/etc/crontab</key>
<true/>
</dict>
</dict>
<key>QueueDirectories</key>
<array>
<string>/usr/lib/cron/tabs</string>
</array>
<key>EnableTransactions</key>
<true/>
</dict>
</plist>
逐行看了下,发现“/etc/crontab”竟然不存在!!!这就是导致crontab没有真正运行的原因,KeepAlive的PathState这个配置大概意思就是,通过判断配置项文件是否存在,来服务保活。于是,按照下面这个模板创建了“/etc/crontab”
Plain textCopy to clipboardOpen code in new windowEnlighterJS 3 Syntax HighlighterSHELL=/bin/bashPATH=/sbin:/bin:/usr/sbin:/usr/binMAILTO=root# For details see man 4 crontabs# Example of job definition:# .---------------- minute (0 - 59)# | .------------- hour (0 - 23)# | | .---------- day of month (1 - 31)# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat# | | | | |# * * * * * user-name command to be executedSHELL=/bin/bash PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root # For details see man 4 crontabs # Example of job definition: # .---------------- minute (0 - 59) # | .------------- hour (0 - 23) # | | .---------- day of month (1 - 31) # | | | .------- month (1 - 12) OR jan,feb,mar,apr ... # | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat # | | | | | # * * * * * user-name command to be executed
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# For details see man 4 crontabs
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed
此时,crontab服务正常!(如果未自动执行,尝试重启Mac)可是,编写cron任务还是不正常,这时再次排查cron错误日志
3、crontab服务正常后,再次排查cron相关日志
查看“/var/log/cron.log”,并没有发现什么有效信息。
因为cron如果执行存在问题,默认会通过MTA服务给系统管理员发邮件,这里我并没有配置什么邮箱,可以直接查看“/var/mail/humh”,其中humh是我当前登录用户,这里记录了需要发送邮件的内容。果然,原来是我cron任务写的有问题!关键信息如下:
我的cron任务是想着将标准输出和错误输出都追加到特定日志中,所以直接用了“&>>”。但我不知道“&>>”这种方式,是bash 3.6.5及之后版本才支持的特性。
截取自https://tiswww.case.edu/php/chet/bash/bashref.html
查看我Mac的bash版本
Plain textCopy to clipboardOpen code in new windowEnlighterJS 3 Syntax Highlighter humh@MacBook-Pro ~ bash --versionGNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17)Copyright (C) 2007 Free Software Foundation, Inc. humh@MacBook-Pro ~ bash --version GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17) Copyright (C) 2007 Free Software Foundation, Inc.
humh@MacBook-Pro ~ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17)
Copyright (C) 2007 Free Software Foundation, Inc.
显然版本不支持,于是改回旧方式,正常!!
Plain textCopy to clipboardOpen code in new windowEnlighterJS 3 Syntax Highlighter* * * * * cd /Users/humh/cron_job_sh && ./keep_vpn.sh >> /Users/humh/cron_job_sh/keep_vpn.log 2>&1* * * * * cd /Users/humh/cron_job_sh && ./keep_vpn.sh >> /Users/humh/cron_job_sh/keep_vpn.log 2>&1
* * * * * cd /Users/humh/cron_job_sh && ./keep_vpn.sh >> /Users/humh/cron_job_sh/keep_vpn.log 2>&1
至此,此次排查结束,问题解决。。。
本文参考
- 迷之 crontab 异常:不运行、不报错、无日志:https://my.oschina.net/leejun2005/blog/1788342
- Mac执行定时任务之Launchctl:https://www.jianshu.com/p/b65c1d339eec
- Bash Reference Manual:https://tiswww.case.edu/php/chet/bash/bashref.html