Mac 自定义定时任务
前言
iCloud 貌似只能同步文稿和桌面的内容,而我 SSH 连接的 config 和密钥都存在
~/.ssh
下,故写了个备份脚本,把~/.ssh
下的内容复制到~/Documents/backupssh
下1
2
3
4
5# 前往根目录
cd ~
# 创建并编辑脚本
nano backupssh.sh脚本内容如下
1
2
rsync -av --update --delete --checksum ~/.ssh/ ~/Documents/backupssh/sshcontrol + X 退出,输入
y
回车保存使用
launchd
方式配置启动任务及定时任务Ref: https://hanleylee.com/articles/manage-process-and-timed-task-by-launchd/
什么是 launchd
launchd 是 MacOS 用来管理系统和用户级别的守护进程的工具. 该工具由两部分组成:
launchd
,该工具主要有两个功能:- 启动系统
- 加载和维护服务
launchctl
: 是launchd
提供的用于对用户交互的工具
对于 launchd
来说,每一个 plist
文件即为一个任务。系统启动时,launchd
会加载 /System/Library/LaunchDaemons
和 /Library/LaunchDaemons
中的所有 plist 文件,用户登录后,会扫描 /System/Library/LaunchAgents
、/Library/LaunchAgents
、~/Library/LaunchAgents
这三个目录的文件并加载它们
plist 放在不同位置时的区别
对于 launchd 来说,有如下五个路径的 plist 文件会被读取加载,他们被触发的时机并不相同,总结如下:
位置 | 类型 | 以什么用户权限运行 | 运行时机 | Provided |
---|---|---|---|---|
/System/Library/LaunchDaemons |
System Daemons | root / 指定用户 | 开机时 | Apple |
/System/Library/LaunchAgents |
System Agents | 当前登录用户 | 任意用户登录 | Apple |
/Library/LaunchDaemons |
Global Daemons | root / 指定用户 | 开机时 | Administrator |
/Library/LaunchAgents |
Global Agents | 当前登录用户 | 任意用户登录 | Administrator |
~/Library/LaunchAgents |
User Agents | 当前登录用户 | 指定用户登录时 | User |
总的来说,LaunchDaemons
和 LaunchAgents
主要有以下两个区别:
- 运行时机
LaunchDaemons
在按下开机按钮后,用户还未输入密码时,就已经运行了LaunchAgents
在用户输入密码后,才开始运行
- 运行用户
LaunchDaemons
是以 root / 其他指定用户运行LaunchAgents
是以当前登录用户的权限运行
实操
一般是我们的备份脚本肯定是要在还未输入密码时就运行了,那就直接来到
/Library/LaunchDaemons
,写个每天 23:00 执行的定时任务1
2
3
4
5# 前往文件夹
cd /Library/LaunchDaemons
# 编辑文件
sudo nano com.lan.backupssh.plistplist 内容如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.lan.backupssh</string>
<key>ProgramArguments</key>
<array>
<string>/Users/lan/backupssh.sh</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Minute</key>
<integer>00</integer>
<key>Hour</key>
<integer>23</integer>
</dict>
<key>KeepAlive</key>
<false/>
<key>StandardOutPath</key>
<string>/Users/lan/Documents/backupssh/ssh/logs/backupssh.log</string>
<key>StandardErrorPath</key>
<string>/Users/lan/Documents/backupssh/ssh/logs/backupssh-err.err</string>
</dict>
</plist>control + X 退出,输入
y
回车保存加载服务:
sudo launchctl bootstrap gui/501 /Library/LaunchDaemons/com.lan.backupssh.plist
启动服务:
sudo launchctl start com.lan.backupssh
检查是否成功启用:
launchctl list | grep 'com.lan'
踩坑
重启之后不知道为什么没有自动启用服务
排除了半天问题也没瞧出个什么名堂,索性还是丢
/Library/LaunchAgents
吧1
2
3
4
5# 复制文件
sudo cp /Library/LaunchDaemons/com.lan.backupssh.plist /Library/LaunchAgents
# 删除源文件
sudo rm -rf /Library/LaunchDaemons/com.lan.backupssh.plist- 加载服务:
launchctl load -w /Library/LaunchAgents/com.lan.backupssh.plist
- 启动服务:
sudo launchctl start com.lan.backupssh
- 检查是否成功启用:
launchctl list | grep 'com.lan'
- 重启之后检查是否启用成功:
launchctl list | grep 'com.lan'
- 加载服务:
后话
KEYS
上述实例中, 我们使用到了 LABEL
,ProgramArguments
之类的 KEYS,常用的 KEYS 如下:
Label
:标签的值,不能与其他 plist 文件中的Label
标签中的值完全重复ProgramArguments
: 标签中放入 shell 所在的路径,用于控制在指定的时间执行 shellRunAtLoad
:表示加载定时任务即开始执行脚本KeepAlive
:是否设置程序是一直存活着,如果退出就重启Disabled
:指定默认情况下该服务是否应该被加载,如果用户使用launchctl disable
/launchctl enable
设置了服务状态,那么此值会被忽略StartInterval
:定时任务的周期,单位为秒StartCalendarInterval
:指定命令在多少分钟、小时、 天、 星期几,、月时间上执行StandardOutPath
:填写脚本运行日志输出的路径StandardErrorPath
:填写脚本运行错误日志输出的路径WorkingDirectory
:设置工作目录UserName
:只有指定用户下才会执行(只在 Daemons 可用)GroupName
:只有指定用户组下才会执行(只在 Daemons 可用)
全部 KEYS 可使用 man 5 launchd.plist
查看;使用 plutil -lint xxx.plist
可以验证 plist 文件的正确性
常用 launchctl 命令
launchctl
是 launchd
提供的用于对用户交互的工具,我们可以方便的 启动/停止/启用/禁用 相关服务。
使用 man launchctl
可以查看到具体用法,其中使用了大量的 specifier
作为命令一部分,主要可以分为三种:
service-name
:服务名,如com.apple.example
domain-target
:域名,如gui/501
,其中 501 可以通过launchctl manageruid
获取service-target
:指定域名下的服务名,是服务名与域名的结合,如gui/501/com.apple.example
我将实际经常会用到的使用命令列出如下:
- 加载 / 卸载:卸载加载是启动的前提,只有加载了之后才能执行任务
launchctl bootstrap gui/501 ~/Library/LaunchAgents/com.hanleylee.test_timer.plist
:加载 指定服务launchctl bootout gui/501 ~/Library/LaunchAgents/com.hanleylee.test_timer.plist
:卸载 指定服务launchctl load <path_of_plist>
:加载 一个 plist 文件,只会加载没有被 disable 的任务, 添加-w
会 enable 状态并加载,这导致下次启动也会加载该任务launchctl unload <path_of_plist>
:停止并 卸载 一个 plist 任务,添加-w
会 disable 状态,这导致下次启动也不会加载该任务launchctl unload <path_of_plist> && launchctl load <path_of_plist>
:修改配置后重载配置,如果任务被修改了。那么必须先 unload,再重新 loadlaunchctl remove <label>
:通过服务名进行 卸载
- 启动 / 停止:启动与停止只会影响当次执行的任务,不会影响下次的计时任务执行
launchctl start <label>
:在不修改 Disabled 状态的前提下根据service_name
值 启动 一个已加载的 service(效果为立即执行, 无论时间是否符合条件)launchctl stop <label>
:在不修改 Disabled 状态的前提下根据service_name
值 停止 一个正在执行中的任务(不会影响其下次的定时启动功能, 只会取消当前执行的当次行为)
- 启用 / 禁用:表示该服务下次启动后会不会被加载,不会影响当前已加载的服务
launchctl enable gui/501/com.hanleylee.test_timer
:启用服务,启用之后再次启动系统会加载该服务launchctl disable gui/501/com.hanleylee.test_timer
:禁用服务,禁用之后再次启动系统也不会加载该服务了
- 杀掉服务
launchctl kill gui/501/com.hanleylee.xuexiqiangguo
launchctl kickstart -k <path_of_plist>
:杀死进程并重启服务,对一些停止响应的服务有效
- Other
launchctl print gui/501
launchctl print-disabled gui/501
launchctl list
:列出已加载的所有服务launchctl list | grep 'com.hello'
:筛选任务列表
相关注意事项:
- 一个服务,必须在被加载后才能使用 start 进行启动,如果使用了
RunAtLoad
或KeepAlive
则在加载时就启动 - 在执行 start 和 unload 前,任务必须先 load 过,否则报错
GUI 工具
如果对每次都要敲以上命令感到恐惧的话,可以使用一款好用的 GUI 软件:https://www.soma-zone.com/LaunchControl/
这款软件基本涵盖了以上全部命令操作,可视化的方式让你更方便一览当前所有定时任务的状态