定制Mac的启动任务

在Mac上设置启动任务可以通过一下两种方式(我实验成功了这两种):

  • 通过系统设置界面设置
  • 利用launchd设置

系统设置界面

1、创建要运行的脚本 (run.sh)

1
2
#!/bin/sh
ls > ~/a.txt

设置脚本运行权限

1
chmod +x run.sh

2、设置

设置脚本打开方式

右击脚本->显示简介->打开方式-> 可以选terminal

系统偏好设置->用户与群众->当前用户->登陆项->+添加上面所创建的脚本

上面操作完成后,系统在每次重新启动后就会自动调用脚本

利用launchd设置

launchd 是 Mac 下用于初始化操作系统的关键进程。通过启动硬盘指定目录下的配置文件,来完成启动任务。这些文件为 plist,本质上是 XML。

目录配置

Mac 下 Launchd 指定目录有:

~/Library/LaunchAgents

/Library/LaunchAgents

/Library/LaunchDaemons

/System/Library/LaunchAgents

/System/Library/LaunchDaemons

其中的区别:

/System/Library 目录下存放的是系统文件

/Library 目录是系统管理员存放的第三方软件

~/Library/ 目录是用户存放的第三方软件

LaunchDaemons 是用户未登陆前就启动的服务

LaunchAgents 是用户登陆后启动的服务

Plist 配置

这里列举几个比较有用的配置关键字:

Label - 标识符,用来表示该任务的唯一性(与plist名称一致)

Program - 程序名称,用来说明运行哪个程序、脚本

ProgramArguments - 数组程序名,同上,只是可以运行多个程序

WatchPaths - 监控路径,当路径文件有变化是运行程序,也是数组

RunAtLoad - 是否在加载的同时

StartInterval - 间隔执行的时间,单位是秒

StartCalendarInterval - 运行的时间,单个时间点使用字典,多个时间点使用数组中包含字典的形式

AbandonProcessGroup - 在job结束时,launchd将杀死所有与这个job相同group ID的所有进程。将这个值设置为true后这个功能将失效

更多配置可以查看苹果文档

实例

  1. 设置定时任务

进入 ~/Library/LaunchAgents创建一个plist文件com.leoliu.launch.awake.plist,

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//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.leoliu.launch.awake</string>
<key>ProgramArguments</key>
<array>
<string>/Users/lbq/Documents/Code/LL_Workspace/DMShell/awake.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StartInterval</key>
<integer>5</integer>
</dict>
</plist>

执行下面的loadstart命令就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cd ~/Library/LaunchAgents

# 加载任务, -w选项会将plist文件中无效的key覆盖掉,建议加上
$ launchctl load -w com.leoliu.launch.awake.plist

# 删除任务
$ launchctl unload -w com.leoliu.launch.awake.plist

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

# 开始任务
$ launchctl start com.leoliu.launch.awake.plist

# 结束任务
$ launchctl stop com.leoliu.launch.awake.plist
  1. 用户唤醒屏幕后自动运行脚本

awake.sh这个脚本的功能是发送一封邮件,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
day=$(date +"%u")
if [ $day -eq 2 ]
then

say 你好,欢迎登录
email_date=$(date "+%Y-%m-%d_%H:%M:%S")
email_body="周天有人在${email_date} 登陆了你的电脑"
email_subject="入侵警告!!!"
# 接收放的邮箱
email_to="xxxx@xxx"
echo "xx密码不能让你们看xx" | (sudo echo $email_body | mail -s $email_subject $email_to)

if [ $? == 0 ]
then
user=$(whoami)
echo -e "邮件发送成功\n $email_body $email_to $user" > ~/Documents/Code/LL_Workspace/DMShell/1aa.txt
# ls > /Users/lbq/Documents/Code/LL_Workspace/DMShell/1aa.txt
else
echo -e "发送失败\n" > ~/Documents/Code/LL_Workspace/DMShell/1aa.txt
fi

else
echo -e "失败\n" > ~/Documents/Code/LL_Workspace/DMShell/1aa.txt
fi

因为每次由睡眠到唤醒(屏幕亮起)后,/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist文件会发生变化,所以可以通过观察该文件的变化,来确定是否唤起。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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.leoliu.launch.awake</string>
<key>ProgramArguments</key>
<array>
<string>/Users/lbq/Documents/Code/LL_Workspace/DMShell/awake.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>WatchPaths</key>
<array>
<string>/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist</string>
</array>
</dict>
</plist>

然后执行:

1
2
$ launchctl load -w com.leoliu.launch.awake.plist
$ launchctl start com.leoliu.launch.awake.plist

执行之后,发现并没有收到邮件,那是因为脚本执行结束后,launchd将杀死所有与当前脚本拥有相同group ID的所有进程。我们需要在com.leoliu.launch.awake.plist文件中加入下面内容:

1
2
<key>AbandonProcessGroup</key>
<true/>

按快捷键option+command+关机键休眠后,在重新唤起,输入密码就会看到结果

参考

mac环境下开机自启动Shell脚本

利用 Launchd 定制 Mac 启动任务

Daemons and Services Programming Guide

launchd.plist说明

Mac OS X: Creating a login hook

Running script upon login mac [closed]