Mac定时任务实现自动化Git提交

OSX系统添加定时任务

Mac 有两种方式可以添加定时任务:

1、launchctl 定时任务(推荐)

2、crontab命令

由于crontab可能会遇到用户交互权限问题导致execution error, crontab 来配置定时任务很容易出错。在 Mac 中,配置定时任务有更好的选择。plist对于MAC上系统交互的操作更支持,plist可以设置到秒,而crontab只能到分钟等原因,故选择使用plist方式实现。

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定义的守护进程任务项

我们随便点看查看一些其中的任务配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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>org.getlantern</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/Lantern.app/Contents/MacOS/lantern</string>
<string>-startup</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>

解读:

Label 对应的 org.getlantern 表示任务名称,必须唯一。
ProgramArguments 表示程序参数,数组的形式,第一个为 需要执行的程序或者脚本等,这里的 /Applications/Lantern.app/Contents/MacOS/lantern-startup 意味着打开程序 lantern
RunAtLoad 是个布尔值,表示开启自启项。

因此,该任务配置表示:设置 lantern 应用为开机自起项。

进入~/Library/LaunchAgents,创建一个plist文件com.github.autocommit.plist

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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.autotask.default</string>
<key>ProgramArguments</key>
<array>
<string>/Users/demo/run.sh</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Minute</key>
<integer>45</integer>
<key>Hour</key>
<integer>11</integer>
</dict>
</dict>
</plist>

3. 加载命令

1
launchctl load -w com.autotask.default.plist
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 加载任务
$ launchctl load -w com.autotask.default.plist

// 删除任务
$ launchctl unload com.autotask.default.plist

// 查看任务列表, 使用 grep '任务部分名字' 过滤
$ launchctl list | grep 'com.autotask.default'

// 开始
$ launchctl start com.autotask.default.plist

// 停止-停止任务。如果将任务已经是运行状态,则作业可能会立即重新启动。
$ launchctl stop com.autotask.default.plist
1
2
3
4
5
6
7
8
9
launchctl remove:从launchd中异步删除任务,在返回之前它不会等待作业实际停止,因此不会对任何错误做处理
launchctl unload:停止并卸载任务,但该任务仍将在下次登录/重新启动时重新启动
launchctl unload -w <路径>:停止并卸载和禁用任务。该任务将不会在下次登录/重新启动时重新启动。

其他几个相关命令

launchctl stop:停止任务。如果将任务已经是运行状态,则作业可能会立即重新启动。
launchctl load <路径>:只要未“禁用”该任务,就加载并启动任务。
launchctl load -w <路径>:加载并启动任务,同时还将任务标记为“未禁用”。任务将在下次登录/重新启动时重新启动。

http://www.wu.run/2019/03/27/mac-launchctl-guidance/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<?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>

1、Label:对应的需要保证全局唯一性;
2、Program:要运行的程序;
3、ProgramArguments:命令语句
4、StartCalendarInterval:运行的时间,单个时间点使用dict,多个时间点使用 array
5、StartInterval:时间间隔,与StartCalendarInterval使用其一,单位为秒,设置执行的时间间隔,单位为秒

多个时间任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<key>StartCalendarInterval</key>
<array>
<dict>
<key>Weekday</key> <!-- 周几 -->
<integer>1</integer>
<key>Hour</key> <!-- 小时 -->
<integer>8</integer>
<key>Minute</key> <!-- 分钟 -->
<string>58</string>
</dict>
<dict>
<key>Weekday</key>
<integer>2</integer>
<key>Hour</key>
<integer>8</integer>
<key>Minute</key>
<string>52</string>
</dict>
</array>

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- 这个表示每个小时的0分钟会执行此任务 -->
<key>StartCalendarInterval</key>
<dict>
<key>Minute</key>
<integer>0</integer>
</dict>
<!-- 在每天的3:55会执行此任务 -->
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>3</integer>
<key>Minute</key>
<integer>55</integer>
</dict>
<!-- 在每六的3:15会执行此任务 -->
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>3</integer>
<key>Minute</key>
<integer>15</integer>
<key>Weekday</key>
<integer>6</integer>
</dict>

3、验证脚本的正确性

你可以将执行时间设置为较近的时间,也可以使用下面语句直接执行一次脚本:

1
2
// 开始
$ launchctl start com.test.task.plist

WorkingDirectory

1
2
cmdStr: `${publicDirPath}/Autotask.py --bunldeid ${Bunlde_ID} --startinterval ${StartInterval} --scriptpath ${this.Script_Path}`

https://imchenway.com/2021/02/24/Mac%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1/

1
2
3
4
5
6
总结一下
虽然plist的设置会复杂很多。但是当前在mac os还是倾向于推荐使用Plist的方法,crontab已不推荐使用。
两者的区别:
1、plist可以设置到秒,而crontab只能到分钟。
2、plist可以同时应用于Mac OS/Iphone。
3、plist对于MAC上系统交互的操作更支持(我就出现过用crontab设置,运行时出现execution error: 不允许用户交互。 (-1713)的错误)

注意事项

Python脚本不执行原因

1、无权限

1
$ chmod 777 task.py

2、正常编译

1
2
3
4
在上述的例子中,python脚本文件最开始的部分有两行说明:

#!/usr/bin/env python // 声明编译环境,即指定编译器
# -*- coding:utf-8 -*- // 编码问题

3、含有第三方库(如:import click)

4、plist文件中的路径以及脚本中的路径都必须是绝对路径

5、验证脚本的正确性

你可以将执行时间设置为较近的时间,也可以使用下面语句直接执行一次脚本:

1
2
// 开始
$ launchctl start com.test.task.plist
文章作者: kyren
文章链接: http://huluo666.github.io/2022/07/12/Mac定时任务实现自动化Git提交/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Kyren's Blog