在 macOS 里,launchd 是系统级的任务调度与服务管理组件。相比把脚本塞进“登录项”或第三方常驻工具,launchd 的优势是:可声明式配置、可随时启停、可查看状态、出问题容易定位,且不会偷偷常驻占资源。
你可以把它们理解成两类“托管单位”:
1)LaunchAgent:跟随当前用户会话启动,适合托盘工具、用户级同步脚本、监听某个目录变化后做处理等。
2)LaunchDaemon:跟随系统启动,通常更早启动、权限更高,适合系统级服务。日常个人教程更推荐从 LaunchAgent 入手。
常用目录:
~/Library/LaunchAgents(当前用户)
/Library/LaunchAgents(所有用户,需管理员权限)
/Library/LaunchDaemons(系统级,需管理员权限)
下面是一个“登录后自动运行一次”的例子。把它保存为:~/Library/LaunchAgents/com.example.hello.plist
<? 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.example.hello</string> <key>ProgramArguments</key> <array> <string>/bin/zsh</string> <string>-lc</string> <string>echo "hello launchd" >> ~/hello-launchd.log</string> </array> <key>RunAtLoad</key> <true/> <key>StandardOutPath</key> <string>/tmp/com.example.hello.out</string> <key>StandardErrorPath</key> <string>/tmp/com.example.hello.err</string> </dict> </plist>
说明:
- Label 必须全局唯一,建议用反域名形式。
- ProgramArguments 推荐写成数组,避免 shell 解析差异;用 zsh -lc 可以让命令在登录 shell 环境下执行,但也意味着环境变量会受 shell 配置影响。
- 通过 StandardOutPath/StandardErrorPath 先把日志落地,调通后再按需收敛。
把文件放进目录后,用下面的命令加载(macOS 新版推荐用 bootstrap):
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.example.hello.plist
查看是否加载成功:
launchctl list | grep com.example.hello
卸载(回滚):
launchctl bootout gui/$(id -u) com.example.hello
如果你不确定命令参数是否匹配当前系统版本,可先用 launchctl help 看一下系统提示,再按需调整。
如果你想每 30 分钟跑一次脚本,可以在 plist 里加入:
<key>StartInterval</key> <integer>1800</integer>
如果你想每天 09:30 跑一次,更适合用日历式:
<key>StartCalendarInterval</key> <dict> <key>Hour</key><integer>9</integer> <key>Minute</key><integer>30</integer> </dict>
提示:别把高频任务写得太激进,尤其是会频繁读写磁盘或拉取网络内容的任务,建议先以“低频 + 可观测”跑通。
1)先看你在 plist 里配置的输出文件,比如:/tmp/com.example.hello.err
2)再用统一日志系统按 Label 搜索(更适合排查启动失败):
log show --last 30m --predicate 'process == "launchd"' --info
3)确认脚本路径与权限:很多“能在终端跑、放进 launchd 就不跑”的问题,根源是 PATH/环境变量不同。建议在 ProgramArguments 里写绝对路径,或在命令里显式设置 PATH。
- 给每个任务单独一个 Label,并把配置文件命名一致,后续排查会非常省心。
- 先做“打印日志/输出到文件”的最小任务,确认调度链路 OK,再替换成真实脚本。
- 需要网络权限/访问钥匙串等能力时,优先用用户级 LaunchAgent;避免不必要地上升到 Daemon。
Apple 官方文档(launchd/启动项概念):https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html
launchd 资料与示例:https://www.launchd.info/