Mac 有两种方式可以添加定时任务:
- crontab 命令
- launchctl 定时任务
crontab 命令
通过crontab 命令,我们可以在固定的间隔时间执行指定的系统指令或 shell script脚本。时间间隔的单位可以是分钟、小时、日、月、周及以上的任意组合。这个命令非常适合周期性的日志分析或数据备份等工作。
命令格式
crontab [-u user] file crontab [-u user] [ -e | -l | -r ]
- -u user:用来设定某个用户的crontab服务;
- file:file是命令文件的名字,表示将file做为crontab的任务列表文件并载入crontab。如果在命令行中没有指定这个文件,crontab命令将接受标准输入(键盘)上键入的命令,并将它们载入crontab。
- -e:编辑某个用户的crontab文件内容。如果不指定用户,则表示编辑当前用户的crontab文件。
- -l:显示某个用户的crontab文件内容,如果不指定用户,则表示显示当前用户的crontab文件内容。
- -r:从/var/spool/cron目录中删除某个用户的crontab文件,如果不指定用户,则默认删除当前用户的crontab文件。
- -i:在删除用户的crontab文件时给确认提示。
crontab 文件格式
分 时 日 月 星期 要运行的命令
- 第1列分钟1~59
- 第2列小时1~23(0表示子夜)
- 第3列日1~31
- 第4列月1~12
- 第5列星期0~7(0和7表示星期天)
- 第6列要运行的命令
备份/恢复 crontab
可以定时备份 crontab 以防误删操作
# 备份 crontab -l > $HOME/.mycron # 恢复 crontab $HOME/.mycron
定时实例
# 每1分钟执行一次myCommand * * * * * myCommand # 每小时的第3和第15分钟执行 3,15 * * * * myCommand # 在上午8点到11点的第3和第15分钟执行 3,15 8-11 * * * myCommand # 每隔两天的上午8点到11点的第3和第15分钟执行 3,15 8-11 */2 * * myCommand # 每周一上午8点到11点的第3和第15分钟执行 3,15 8-11 * * 1 myCommand # 晚上11点到早上7点之间,每隔一小时重启smb * 23-7/1 * * * /etc/init.d/smb restart
注意清理系统用户的邮件日志
每条任务调度执行完毕,系统都会将任务输出信息通过电子邮件的形式发送给当前系统用户,这样日积月累,日志信息会非常大,可能会影响系统的正常运行,因此,将每条任务进行重定向处理非常重要。 例如,可以在crontab文件中设置如下形式,忽略日志输出:
0 */3 * * * /usr/local/apache2/apachectl restart >/dev/null 2>&1
launchctl 定时任务
下面将手把手教你在mac上创建定时任务。(任务目标:每天晚上十点定时执行/Users/demo/helloworld.py的python程序)
1. 创建run.sh脚本
进入 helloworld.py程序所在目录
cd /User/demo
创建run.sh脚本
vi run.sh
添加执行helloworld.py的命令
#!/bin/sh # 记录一下开始时间 echo `date` >> /Users/demo/log && # 进入helloworld.py程序所在目录 cd /Users/demo && # 执行python脚本(注意前面要指定python运行环境/usr/bin/python,根据自己的情况改变) /usr/bin/python helloworld.py # 运行完成 echo 'finish' >> /Users/demo/log :wq保存退出
注意,脚本要改成可执行的权限
chmod 777 run.sh
2. 编写plist文件
launchctl 将根据plist文件的信息来启动任务。
plist脚本一般存放在以下目录:
/Library/LaunchDaemons -->只要系统启动了,哪怕用户不登陆系统也会被执行
/Library/LaunchAgents -->当用户登陆系统后才会被执行
更多的plist存放目录:
~/Library/LaunchAgents :由用户自己定义的任务项 /Library/LaunchAgents :由管理员为用户定义的任务项 /Library/LaunchDaemons :由管理员定义的守护进程任务项 /System/Library/LaunchAgents :由Mac OS X为用户定义的任务项 /System/Library/LaunchDaemons :由Mac OS X定义的守护进程任务项
进入~/Library/LaunchAgents,创建一个plist文件com.demo.plist
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <!-- Label唯一的标识 --> <key>Label</key> <string>com.demo.plist</string> <!-- 指定要运行的脚本 --> <key>ProgramArguments</key> <array> <string>/Users/demo/run.sh</string> </array> <!-- 指定要运行的时间 --> <key>StartCalendarInterval</key> <dict> <key>Minute</key> <integer>00</integer> <key>Hour</key> <integer>22</integer> </dict> <!-- 标准输出文件 --> <key>StandardOutPath</key> <string>/Users/demo/run.log</string> <!-- 标准错误输出文件,错误日志 --> <key>StandardErrorPath</key> <string>/Users/demo/run.err</string> </dict> </plist>
launchctl 通过配置文件指定执行周期和任务,不同于 crontab,launchctl 的最小时间间隔是 1s。
plist 文件存放路径为/Library/LaunchAgents或/Library/LaunchDaemons,前者仅当用户登陆后才被执行,后者只要系统启动就会被执行。
支持两种方式配置执行时间:
- StartInterval: 指定脚本每间隔多长时间(单位:秒)执行一次;
- StartCalendarInterval: 可以指定脚本在多少分钟、小时、天、星期几、月时间上执行,类似如crontab的中的设置,包含下面的 key:
Minute <integer> The minute on which this job will be run. Hour <integer> The hour on which this job will be run. Day <integer> The day on which this job will be run. Weekday <integer> The weekday on which this job will be run (0 and 7 are Sunday). Month <integer> The month on which this job will be run.
3. 加载命令
launchctl load -w com.demo.plist
这样任务就加载成功了。
# 加载任务, -w选项会将plist文件中无效的key覆盖掉,建议加上 $ launchctl load -w com.demo.plist # 删除任务 $ launchctl unload -w com.demo.plist # 查看任务列表, 使用 grep '任务部分名字' 过滤 $ launchctl list | grep 'com.demo' # 开始任务 $ launchctl start com.demo.plist # 结束任务 $ launchctl stop com.demo.plist
plist 文件的字段说明
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <!-- 名称,要全局唯一 --> <key>Label</key> <string>com.uniflor.notifier</string> <!-- 要运行的程序, 如果省略这个选项,会把ProgramArguments的第一个 元素作为要运行的程序 --> <key>Program</key> <string>/Users/uniflor/script.sh</string> <!-- 命令, 第一个为命令,其它为参数--> <key>ProgramArguments</key> <array> <string>/Users/uniflor/script.sh</string> </array> <!-- 运行时间 --> <key>StartCalendarInterval</key> <dict> <key>Minute</key> <integer>30</integer> <key>Hour</key> <integer>9</integer> <key>Day</key> <integer>1</integer> <key>Month</key> <integer>5</integer> <!-- 0和7都指星期天 --> <key>Weekday</key> <integer>0</integer> </dict> <!-- 运行间隔,与StartCalenderInterval使用其一,单位为秒 --> <key>StartInterval</key> <integer>30</integer> <!-- 标准输入文件 --> <key>StandardInPath</key> <string>/Users/uniflor/run-in.log</string> <!-- 标准输出文件 --> <key>StandardOutPath</key> <string>/Users/uniflor/Bin/run-out.log</string> <!-- 标准错误输出文件 --> <key>StandardErrorPath</key> <string>/Users/uniflor/Bin/run-err.log</string> </dict> </plist>
配置文件
进入到~/Library/LaunchAgents下建一个plist文件com.test.launchctl.plist,内容如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.test.launchctl.plist</string> <key>ProgramArguments</key> <array> <string>/usr/bin/python</string> <string>/Workspace/test.py</string> </array> <key>StartCalendarInterval</key> <dict> <key>Minute</key> <integer>4</integer> <key>Hour</key> <integer>13</integer> </dict> <key>KeepAlive</key> <false/> <key>RunAtLoad</key> <true/> <key>StandardOutPath</key> <string>/tmp/AutoMakeLog.log</string> <key>StandardErrorPath</key> <string>/tmp/AutoMakeLog.err</string> </dict> </plist> StartCalendarInterval: 执行周期 RunAtLoad: 加载时执行一次 StandardOutPath: 标准输出路径 StandardErrorPath: 错误输出路径
管理定时任务
加载任务
cd ~/Library/LaunchAgents launchctl load com.test.launchctl.plist
卸载任务
launchctl unload com.felink.gitmirror.plist
立即执行一次任务
launchctl start ccom.test.launchctl.plist
停止任务
launchctl stop ccom.test.launchctl.plist
写个简单的applescript。
on callmeican() set meican_url to "https://meican.com" as string tell application "Google Chrome" open location meican_url activate end tell end callmeican say "不要这么拼了,预订美餐时间到了" display dialog "不要这么拼了,预订美餐时间到了(截至时间16:30)!" buttons {"好的", "我不定了"} default button 1 if the button returned of the result is "好的" then -- action for 1st button goes here callmeican() end if
脚本的作用大概是MAC会通过弹窗和语音提醒我该订餐了,如果选择定,就自动用浏览器打开订餐的页面。这个脚本每天在四点执行。
1、使用crontab设置定时任务
crontab -e 或者sudo crontab -e。 00 16 * * * osascript /Users/hanlingzhi/project/applescript/meican.scpt
输入完成后,保存退出。系统自动建立新cron,提示如下:crontab: installing new crontab。设置非常简单。
2、使用苹果的Schedule jobs using launchd设置定时任务。需要写一个plist文件,描述任务的动作、间隔的时间、日志输出等参数。
我创建一个plist文件com.hanlingzhi.cron.meican.plist,大概内容如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <!-- 名称,要全局唯一 --> <key>Label</key> <string>com.hanlingzhi.cron.meican</string> <!-- 命令, 第一个为命令,其它为参数--> <key>ProgramArguments</key> <array> <string>osascript</string> <string>/Users/hanlingzhi/project/applescript/meican.scpt</string> </array> <!-- 运行时间 --> <key>StartCalendarInterval</key> <dict> <key>Minute</key> <integer>0</integer> <key>Hour</key> <integer>16</integer> </dict> <!-- 标准输入文件 --> <key>StandardInPath</key> <string>/Users/hanlingzhi/project/applescript/log/run-in-meican.log</string> <!-- 标准输出文件 --> <key>StandardOutPath</key> <string>/Users/hanlingzhi/project/applescript/log/run-out-meican.log</string> <!-- 标准错误输出文件 --> <key>StandardErrorPath</key> <string>/Users/hanlingzhi/project/applescript/log/run-err-meican.log</string> </dict> </plist>
然后将plist文件放在/Users/hanlingzhi/Library/LaunchAgents,你的用户目录下,然后执行launchctl load plist就可以启动了。
plist脚本存放路径为/Library/LaunchDaemons或用户目录/Library/LaunchAgents,其区别是后一个路径的脚本当用户登陆系统后才会被执行,前一个只要系统启动了,哪怕用户不登陆系统也会被执行。
可以通过两种方式来设置脚本的执行时间。一个是使用StartInterval,它指定脚本每间隔多长时间(单位:秒)执行一次;另外一个使用StartCalendarInterval,它可以指定脚本在多少分钟、小时、天、星期几、月时间上执行,类似如crontab的中的设置。
<key>StartInterval</key> <integer>3600</integer> 或者 <key>StartCalendarInterval</key> <dict> <key>Minute</key> <integer>30</integer> <key>Hour</key> <integer>9</integer> </dict>
launchctl的命令使用大家看一下帮助文档。
由于操作还是比较复杂,为了帮助快速执行,写了个shell快速拷贝新的plist并完成服务重启
__path='/Users/hanlingzhi/project/applescript' __plist_path=${__path}/plist __launchagents_path='/Users/hanlingzhi/Library/LaunchAgents' # 拷贝plist到用户自己定义的任务项目录 cp -rf ${__plist_path}/* ${__launchagents_path} # 根据plist中的文件遍历load for plist_file in `ls ${__plist_path}` do echo "重启${plist_file}定时任务" launchctl unload ${__launchagents_path}/${plist_file} sleep 0.5 launchctl load ${__launchagents_path}/${plist_file} task_name=`echo ${plist_file} | sed 's/.plist//g'` launchctl list | grep ${task_name} done
总结一下
虽然plist的设置会复杂很多。但是当前在mac os还是倾向于推荐使用Plist的方法,crontab已不推荐使用。
两者的区别:
1、plist可以设置到秒,而crontab只能到分钟。
2、plist可以同时应用于Mac OS/Iphone。
3、plist对于MAC上系统交互的操作更支持(我就出现过用crontab设置,运行时出现execution error: 不允许用户交互。 (-1713)的错误