macOS 使用 launchctl 和 crontab 设置定时任务的方法

目录
[隐藏]

下面的介绍以该目标为例:

设置每天早上 9 点定时执行 /Users/lzwme/schedule/helloworld.cjsNode.js 程序。
日志记录目录: /Users/lzwme/schedule/logs

1 基于 launchctl 设置定时任务

launchctl 是一个统一的服务管理框架,可以启动、停止和管理守护进程、应用程序、进程和脚本等。
launchctl 是通过配置文件来指定执行周期和任务的。

1.1 创建 run.sh 脚本

执行 vi /Users/lzwme/schedule/run.sh,然后参考输入如下内容:

#!/bin/sh

# 默认不会加载环境变量。主动加载一下
source ~/.bashrc_profile
# 加载 homebrew 环境变量
export PATH=/opt/homebrew/bin:$PATH
# 这里使用了 fnm 管理 node.js 版本,主动加载 fnm bin 所在目录到PATH环境变量
# 注意修改为你的需要设置,并修改实际的安装路径
export PATH=/Users/lzwme/Library/Caches/fnm_multishells/1295_1704804685161/bin:$PATH

# 记录一下开始时间
date

# 进入helloworld.cjs程序所在目录
cd /Users/lzwme/schedule

# 执行 helloworld.cjs 程序
node helloworld.cjs

# 运行完成
echo 'end'

保存退出后,然后为脚本设置可执行权限:

chmod 777 /Users/lzwme/schedule/run.sh

1.2 编写 plist 文件

launchctl 根据 plist 文件的信息来启动任务。plist 脚本一般存放在以下目录:

  • /Library/LaunchDaemons 只要系统启动了都会执行,即使用户没有登录
  • /Library/LaunchAgents 当用户登陆系统后才会被执行

更多的 plist 存放目录:

  • ~/Library/LaunchAgents 由用户自己定义的任务项
  • /Library/LaunchAgents 由管理员为用户定义的任务项
  • /Library/LaunchDaemons 由管理员定义的守护进程任务项
  • /System/Library/LaunchAgentsMac OS X 为用户定义的任务项
  • /System/Library/LaunchDaemonsMac OS X 定义的守护进程任务项

我们这里进入 ~/Library/LaunchAgents,创建一个 plist 文件 com.lzw.me.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>
  <!-- Label唯一的标识 -->
  <key>Label</key>
  <string>com.lzw.me.plist</string>
  <!-- 是否加载时立即执行 -->
  <key>RunAtLoad</key>
  <false/>
  <!-- 命令语句,指定要运行的脚本 -->
  <key>ProgramArguments</key>
  <array>
    <string>/Users/lzwme/run.sh</string>
  </array>
  <!-- 指定要运行的时间 -->
  <key>StartCalendarInterval</key>
  <array>
    <dict>
      <!-- 指定每天9:00执行 -->
      <key>Hour</key>
      <integer>09</integer>
      <key>Minute</key>
      <integer>00</integer>
    </dict>
    <dict>
      <!-- 指定周日 17:00 也执行一次 -->
      <key>Weekday</key>
      <integer>0</integer>
      <key>Hour</key>
      <integer>17</integer>
      <key>Minute</key>
      <string>00</string>
    </dict>
  </array>
<!-- 标准输出文件 -->
<key>StandardOutPath</key>
<string>/Users/lzwme/schedule/logs/run.log</string>
<!-- 标准错误输出文件,错误日志 -->
<key>StandardErrorPath</key>
<string>/Users/lzwme/schedule/logs/run.err.log</string>
</dict>
</plist>

关于 plist 文件的配置

plist 支持两种方式配置执行时间:

  • StartInterval: 指定每间隔多长时间(单位秒)执行一次
  • StartCalendarInterval: 指定脚本在多少分钟、小时、天、星期几、月时间上执行,类似 crontab 的设置,包含以下 key:
    • Minute The minute on which this job will be run.
    • Hour The hour on which this job will be run.
    • Day The day on which this job will be run.
    • Weekday The weekday on which this job will be run (0 and 7 are Sunday).
    • Month The month on which this job will be run.

plist 部分参数说明:

  1. Label:对应的需要保证全局唯一性
  2. Program:要运行的程序
  3. ProgramArguments:命令语句
  4. StartCalendarInterval:运行的时间,单个时间点使用dict,多个时间点使用 array
  5. StartInterval:时间间隔,与StartCalendarInterval使用其一,单位为秒
  6. StandardInPath、StandardOutPath、StandardErrorPath:标准的输入输出错误文件。
  7. 定时启动任务时如果涉及到网络,但是电脑处于睡眠状态则执行不了。可以定时的启动屏幕。

1.3 加载命令

launchctl load -w com.lzw.me.plist

这样任务就加载成功了。

更多的命令:

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

# 删除任务
$ launchctl unload -w com.lzw.me.plist

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

# 查看出错信息。`launchctl list` 的第一列为错误码,若不为 0 则表示出错
$ launchctl error [error_code]

# 开始任务
$ launchctl start com.lzw.me.plist

# 结束任务
$ launchctl stop com.lzw.me.plist

说明:

  • 若修改了任务,需先 unloadload 才能生效:launchctl unload -w com.lzw.me.plist && launchctl load -w com.lzw.me.plist
  • start 可以立即执行任务,以方便测试。这点比 crontab 方便(若修改了任务,仍需先 unload && load
  • 执行 startunload 前,任务必须先 load 以加载任务
  • stop 可以停止任务,而无需删除文件

2 MacOS 基于 crontab 设置定时任务

crontab 是一个 Linux 类系统通用的命令行工具,用于设置周期性执行的定时任务。在 MacOS 中,crontab 命令的用法与 Linux 基本一致。但它在 MacOS 上的执行,也是基于 launchctlplist 文件实现的自启动。

2.1 配置 crontab 程序

首先使用 launchctl 查看crontab 程序的启动配置是否已存在:

sudo launchctl list | grep cron

有输出则说明任务存在

然后执行命令查看启动项配置:locate com.vix.cron

若有报错输出,则按提示操作即可。一般未曾操作过,会提示你执行如下命令以加载 locate 文件。执行它即可:

sudo launchctl load -W /System/Library/LaunchDaemons/com.apple.locate.plist

查看 /etc/crontab 文件是否存在,若不存在则创建:

ls /etc/crontab
sudo touch /etc/crontab

2.2 为 cron 程序添加完全磁盘访问权限

OSX 10.15 以上版本,需要为 cron 程序添加完全磁盘访问权限,否则会报错。具体方法为:

执行如下命令,打开 cron 程序所在目录:

# 查看 cron 位置。一般会输出: /usr/sbin/cron
which cron
# 打开该目录
open /usr/sbin

接着依次打开: 系统偏好设置 -> 安全性与隐私 -> 隐私 -> 完全磁盘访问权限。将 /usr/sbin 下的 cron 程序拖动到这里。

2.3 配置定时任务

接下来的操作,就和 Linux 基本一致了。

首先,执行命令添加定时任务:crontab -e

在打开的编辑器中输入任务配置。以上面的目标为例,输入如下内容:

0 09 * * * /Users/lzwme/schedule/run.sh >> /Users/lzwme/schedule/logs/run.log 2>&1;
  • crontab -l 查看已设置的定时任务
  • crontab -r 删除所有定时任务

3 总结与参考

launchctl 是系统级别的工具,可以实现各种需求的实现。但基于 plist 文件的方式相对复杂一些。好处是可以使用 start 命令即时调试,出错时可以很方便的定位。

crontab 的方式配置上比较简单,但调试起来比较麻烦。特别是出错时很难去定位。

点赞 (0)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Captcha Code