macOS 定时任务(自动化任务)
在 macOS 上实现定时任务的推荐方式之一是使用系统自带的 launchd 守护进程。你可以通过配置 plist
文件并使用 launchctl
命令进行任务的加载、卸载和管理。本文将详细介绍使用 launchd
的方法,包含定时执行、系统重启后仍能生效、日志输出等完整配置。
💡 推荐先阅读:「macOS 的守护程序和服务」了解
launchd
的基础知识。
✅ 使用 launchd 实现定时任务
macOS 的守护程序为 launchd,任务的生命周期通过 .plist
配置文件进行管理。
📝 第一步:创建 .plist
配置文件
示例:每天 16:00 执行一个脚本任务,并在系统重启后自动生效:
mkdir -p ~/Library/LaunchAgents
nano ~/Library/LaunchAgents/com.example.run_task.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.example.run_task</string>
<!-- 要执行的命令及参数 -->
<key>ProgramArguments</key>
<array>
<string>/bin/zsh</string>
<string>/path/to/task.sh</string>
</array>
<!-- 每天 16:00 运行 -->
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>16</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<!-- 在加载(如重启后)立即运行一次 -->
<key>RunAtLoad</key>
<true/>
<!-- 日志输出位置(可选) -->
<key>StandardOutPath</key>
<string>/tmp/com.example.task.out</string>
<key>StandardErrorPath</key>
<string>/tmp/com.example.task.err</string>
</dict>
</plist>
⏱ 其他触发方式参考
每隔一小时执行一次任务:
<key>StartInterval</key>
<integer>3600</integer>
每月 7 日的 13:45 执行任务:
<key>StartCalendarInterval</key>
<dict>
<key>Minute</key>
<integer>45</integer>
<key>Hour</key>
<integer>13</integer>
<key>Day</key>
<integer>7</integer>
</dict>
每周一到周五的 9:00 执行任务:
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>9</integer>
<key>Minute</key>
<integer>0</integer>
<key>Weekday</key>
<integer>1</integer> <!-- 1=周一, 7=周日 -->
</dict>
文件或目录变化时触发:
<key>WatchPaths</key>
<array>
<string>/path/to/watch/directory</string>
</array>
更多配置选项详见:Creating Launchd Jobs – Apple Developer
⚙️ 第二步:加载与管理任务
加载任务:
launchctl load ~/Library/LaunchAgents/com.example.run_task.plist
卸载任务:
launchctl unload ~/Library/LaunchAgents/com.example.run_task.plist
⚠️ 修改
.plist
文件后必须先unload
再load
以使更改生效。
启动/停止任务(手动触发):
launchctl start com.example.run_task
launchctl stop com.example.run_task
查看是否加载成功:
launchctl list | grep com.example.run_task
📁 关于配置文件位置
-
~/Library/LaunchAgents
:仅当前用户自动加载(推荐) -
/Library/LaunchAgents
:系统所有用户自动加载(需要sudo
权限) -
/Library/LaunchDaemons
:系统级后台服务,适合无用户登录场景
🔄 如果你更新了配置
launchctl unload ~/Library/LaunchAgents/com.example.run_task.plist
launchctl load ~/Library/LaunchAgents/com.example.run_task.plist
✅ 检查命令路径
确保你使用的可执行文件路径正确,例如:
which bash
# 或
which node
替换 .plist
中的 <string>路径</string>
,避免因路径错误导致任务执行失败。
🌍 环境变量设置
launchd 运行的任务默认环境变量很少,可能导致脚本找不到命令。
推荐解决方法:在脚本中设置 PATH
环境变量
#!/bin/zsh
export PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
export HOME=/Users/yourusername
# 你的脚本内容
📊 日志查看与调试
查看任务状态和日志:
# 查看任务列表和状态
launchctl list | grep com.example.run_task
# 查看系统日志(过滤相关任务)
log show --predicate 'subsystem == "com.apple.launchd"' --last 1h | grep com.example.run_task
# 查看自定义日志文件
tail -f /tmp/com.example.task.out
tail -f /tmp/com.example.task.err
常见状态码含义:
0
:任务成功执行1
:一般错误126
:文件不可执行127
:命令未找到
💡 实际使用案例
案例1:定时备份文件
<?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.example.backup</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/rsync</string>
<string>-av</string>
<string>/Users/username/Documents/</string>
<string>/Volumes/Backup/Documents/</string>
</array>
<!-- 每天凌晨2点执行 -->
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>2</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<key>StandardOutPath</key>
<string>/tmp/backup.out</string>
<key>StandardErrorPath</key>
<string>/tmp/backup.err</string>
</dict>
</plist>
案例2:定时清理临时文件
创建清理脚本 cleanup.sh
:
#!/bin/bash
# 清理临时文件
find /tmp -name "*.tmp" -mtime +7 -delete
find ~/Downloads -name "*.dmg" -mtime +30 -delete
echo "$(date): 清理完成" >> /tmp/cleanup.log
对应的 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.example.cleanup</string>
<key>ProgramArguments</key>
<array>
<string>/bin/zsh</string>
<string>/Users/username/scripts/cleanup.sh</string>
</array>
<!-- 每周日凌晨1点执行 -->
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>1</integer>
<key>Minute</key>
<integer>0</integer>
<key>Weekday</key>
<integer>0</integer>
</dict>
</dict>
</plist>
🗑️ 卸载和删除任务
完全移除定时任务的步骤:
-
卸载任务:
launchctl unload ~/Library/LaunchAgents/com.example.run_task.plist
-
删除配置文件:
rm ~/Library/LaunchAgents/com.example.run_task.plist
-
验证删除:
launchctl list | grep com.example.run_task # 应该没有输出结果
-
清理日志文件(可选):
rm /tmp/com.example.task.out rm /tmp/com.example.task.err
⚠️ 常见问题与解决方案
问题1: 任务不执行
可能原因:
- 脚本路径错误
- 没有执行权限
- 环境变量缺失
解决方案:
# 检查脚本是否存在和可执行
ls -la /path/to/script.sh
chmod +x /path/to/script.sh
# 手动测试脚本
/path/to/script.sh
问题2: 找不到命令 xxx command not found
推荐解决方案: 在 shell 脚本中设置 PATH
环境变量,例如:
export PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
# 剩余脚本内容
问题3: Load failed: 5: Input/output error
可能原因:
- .plist 文件路径错误
- .plist 文件内容错误,例如语法错误(标签错误、标签重复、标签缺失等)
问题4: TERM environment variable not set.
原因: 通过 cron 执行shell脚本任务(例如包含clear
命令),需要通过环境变量配置指定运行时使用的终端,否则会报错。
解决方案:
- 首先查看本地 TERM 环境变量内容
echo $TERM
,例如xterm-256color
- 然后在 .plist 文件中添加 TERM 环境变量,例如:
<key>EnvironmentVariables</key>
<array>
<dict>
<key>TERM</key>
<string>xterm-256color</string>
</dict>
</array>
🔧 高级配置选项
限制资源使用:
<key>SoftResourceLimits</key>
<dict>
<key>NumberOfFiles</key>
<integer>1024</integer>
</dict>
设置工作目录:
<key>WorkingDirectory</key>
<string>/Users/username/workspace</string>
任务失败后自动重启:
<key>KeepAlive</key>
<true/>
只在特定条件下运行:
<key>StartOnMount</key>
<true/>
📚 相关工具和替代方案
除了 launchd,你也可以考虑以下工具:
- pm2:适用于 Node.js 应用的进程管理器
- Automator + Calendar:图形化界面的自动化工具
通过以上步骤,你可以方便地在 macOS 上创建稳定、可靠的定时任务,并支持开机自启与日志记录。
参考资料:
- Apple Developer - Creating Launchd Jobs
- launchd.plist Manual Page
- Schedule jobs using launchd
- Launchd examples