Install ExtensionApp Store

用 CleanMyMac 和 launchd 全面掌控 macOS 启动项 - 少数派

随着 macOS Catalina 的公测版本发布,应该有很多人都升级了系统开始尝鲜。不过每每新系统发布,总会有一些应用出现水土不服的症状,尤其是一些开机就启动的应用如果遇到卡死、频繁退出的问题,就会让人很抓狂。

还有很多人的 Mac 越用越卡,因为有很多不必要开机自启动的应用,还有一些可能是不小心中了恶意软件的招,像是臭名昭著的 MacKeeper。这些软件在开机时自启动,然后在整个系统运行阶段都占据大量的资源。我们最好能够把它们干掉。

当然,管理不仅仅是移除,很多时候,也会有手动添加启动项的需求。例如 LaunchBar 的新用户最常见的问题就是如何开机自动启动。

这篇文章给大家介绍 3 个 macOS 里启动项管理的方法,他们不是完全的替代关系,而是可以相互补充。他们分别是:

  1. macOS 自带的「登录项」管理,可以添加和管理部分自启动应用和文件。
  2. CleanMyMac 的优化模块,比系统的「登录项」能管理的范围更大,而且能管理「启动代理」。
  3. macOS 底层的 launchd 服务,其实就是 CleanMyMac 里控制的「启动代理」,搞明白以后,我们不仅能管理,而且还能添加自定义的服务。

如果想要移除已有的启动项,或者添加现成的应用到启动项目里,看看前两个方法就可以了。如果你对于自启动还有更高的要求,不妨了解一下第三部分的 launchd,它的能力一定不会让你失望。

macOS 自带的登录项管理

大部分情况下,如果你想要禁止特定的开机启动项目应该怎么办?除了应用自身提供设置以外,有经验的朋友知道「系统偏好设置 - 用户与群组 - 登录项(Login Items)」里是可以管理一些启动项目的:

用户与群组的登录项

如果你不希望某个应用程序开机自动启动,将其从这个列表移除就可以。很多应用会在第一次运行以后,自动把自己添加到这个列表里,在以后启动 Mac 的时候,就会随着你的登录自动打开。

同样的,如果应用没有提供开机启动选项,你也可以手动将他们添加进来。添加到这个列表就行,例如 Launchbar,把它的图标拖动到这个列表里,就能开机自启动了。

其实,这个「登录项」里不仅能够添加应用程序,任何文件或目录都可以被添加到这个列表并开机启动。

举个例子,我们团队在办公室共享一个 NAS,大部分共享的文件直接在其上操作就可以了,麻烦的地方在于每次使用之前都要先打开 Finder,将 NAS 的文件目录挂载到本地才能使用。

将 FTP 目录添加到登录项里

其实,只要把 NAS 的目录添加到「登录项」里,开机的时候就自动挂载了,就不需要每次都手动操作。即便这样的网络文件夹设置有密码,也可以在添加登录项的时候就填写好,不需要每次都输入。

虽然「登录项」提供了开机启动的管理,但你一定也会发现,有些应用并不在上面这个列表里,而且也会随着系统的启动而启动。如果这个应用自身又不提供开机启动的设置项,难道我们就没法阻止它开机自启动了吗?

了解 CleanMyMac 的速度优化功能

大名鼎鼎的 CleanMyMac 提供了诸多优化功能,包括文件清理、恶意软件移除、应用卸载等,其中有一个「优化」(Optimization)模块,也包含了启动项目等管理功能。

CleanMyMac 的优化功能

CleanMyMac 的优化模块包含了 4 个功能:

  1. 登录项(Login Items)
  2. 启动代理(Launch Agents)
  3. 挂起的应用程序(Hung Applications)
  4. 占用较多资源的项目(Heavy Consumers)

其中,「登录项」与「启动代理」是本文关注的主要对象;这里的「登录项」尽管和「系统偏好设置」里的名称一样,但如果你仔细观察,会发现 CleanMyMac 的「登录项」里的项目要多出不少。

CleanMyMac 的登录项页面

事实上,CleanMyMac 的「登录项」把所有包含开机自启动设置的应用都包括了进来,例如 1Password 7,尽管它在「系统偏好设置」里没有体现,在应用自身设置里也没有相关设置,但是它就是能开机自启动。你在 CleanMyMac 里就可以看到,并且可以禁用它,将 1Password 替换成其他应用也是一样。

但是仍然有很多启动项目我们没法在这个列表里找到,我们再来看看「启动代理」(Launch Agents),可以看到这个列表里包含了另外一些应用或者应用的服务。以我的 Mac 为例,列表里有这么几个应用相关的启动项目:

  • 搜狗输入法的代理程序
  • Setapp 及其升级检测程序
  • Pritunl 及其代理
  • 若干个 Karabiner 相关的 .plist 文件
CleanMyMac 的启动代理页面

看名称就不难理解,这些代理项目给它们的主应用提供了诸如自动升级、运行守护、数据同步的功能。例如搜狗输入法的词库同步就依赖这个服务来进行,而 Setapp 主应用及通过 Setapp 安装的应用的鉴权、更新都是通过 SetappUpdater.app  这个服务来完成的。

那些我们在「登录项」里找不到的,都可以在「启动代理」里边找到,也可以在这里将其禁用。如果只是想要解决文章开头提出的问题,想要自由地禁用任意启动项目,到这里已经都搞定了。

不过,你是不是也好奇,为什么要区分「登录项」和「启动代理」,他们有什么区别呢?

启动代理与登录项的区别

我们最能感知的差异其实是时序上的,所有启动代理中的项目,理论上都比登录项更早打开。实际上,「启动代理」的项目是随着系统启动而启动的,而「登录项」中的内容则是在系统启动完成以后才打开的,你可以理解为系统替我们双击运行了程序或文件。

能力有限的登录项

它们的区别远不止是启动顺序,更在于它们的能力范围与灵活性差异。登录项尽管已经很方便了,但也有它的局限性。「登录项」可以自动运行一个应用,也可以打开一个文件或目录;但是运行的方式和我们手动双击时无异,只会使用默认的「打开方式」来调用。

举个例子,假设你想在开机时自动运行一个脚本文件,如果你将其添加到「登录项」里,那你只能看到它被一个文本编辑器打开,而不是如你所愿的运行这段脚本。

能力强大的启动代理

而「启动代理」负责的就是这样的事情,它并不直接运行某个应用,而是通过一个个配置文件(.plist)来执行特定的任务,包括可执行的应用程序和各种脚本文件。

因为有配置文件的存在,让 launchd 异常的强大,就我所知,至少可以实现以下 5 种类型的用途:

  1. 确保服务存活,例如提供持续集成服务的 Jenkins 服务,就可以用这个方式保活。
  2. 定时运行任务,例如搜狗输入法每隔 3 秒就检查一次其同步服务是否运行。
  3. 检查硬件连接,例如 Karabiner 用于检测外接设备连接/断开。
  4. 监控文件变化,例如特定文件夹发生变化时,就自动发送一封邮件。
  5. 其它条件触发,例如连上网络时,就自动检测软件是否有更新。

这个列举不够严谨,以上只是 launchd 基础能力的一部分,就像我们喜欢折腾的其它工具一样,它提供了一系列的基础能力,而最终实现的效果更多依靠使用者的想象力与动手能力(配置文件 + 脚本)。这些正是我们在用 Keyboard Maestro、Hazel 这样的工具时,十分需要的一些特性,很难想象其实系统已经蕴含了这样的能力吧?

用 launchd 服务添加自定义启动代理

让我们正式的介绍一下,「启动代理」(Launch Agents)属于 macOS 提供的 launchd 服务的一部分,它的另一部分名叫 Launch Daemon。前者是在用户登录以后开始运行的,后者则在用户登录之前,也就是 Mac 启动到登录界面期间就已经完成启动了。

一个完整的 launchd 实例通常包含 2 个部分:

  1. 包含一定触发条件的 .plist 配置文件
  2. 由 .plist 文件触发的可执行程序或脚本

举个例子,我负责维护一个 Slack 的群组,这个群组产生大量高价值的信息,所以消息记录的留存与检索十分重要。但是 Slack 官方的收费价格对于这个群组来说又过于高昂,所以我基于一个 GitHub 项目维护了一个 Python 脚本,以 bot 的形式给这个 Slack 群组提供消息记录的检索功能。

为了让这个脚本持续不间断运行,即便重启设备也能恢复运行,我就需要有个手段让其「保活」。在 macOS 上,launchd 服务就是最佳人选,我们就以此为例子介绍一下最基本的 .plist 配置文件。

.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>slack.archivebot</string>
    <key>ProgramArguments</key>
    <array>
        <string>/anaconda3/bin/python</string>
        <string>/Users/username/git/archivebot.py</string>
    </array>
    <key>KeepAlive</key>
    <true/>
</dict>
</plist>

这是一个最简单的配置文件,分别解释一下这个文件里涉及的字段:

  • 首尾部分是 .plist 文件通用的,自定义的内容是被 <dict> 和 </dict> 所包括的
  • Label,也就是这个服务的名称,同时会作为文件名。习惯上会包含作者的名称和服务本身的名称,这里我只用了服务名称,比较容易辨识。
  • ProgramArguments,这一项指定了我们要运行的脚本路径;如果不是 Shell 脚本,还得用一个字段来指定运行脚本的类型,这里就是 Python 的程序路径。
  • KeepAlive,这个字段设置为 True 时,launchd 服务就会确保它所执行的脚本始终处于运行状态。

注意:最终执行的脚本或程序的路径建议放置在平时不会用到的地方,避免不小心删改导致出错。

有了这个配置文件,接着我们就要让它运行起来,还有两个步骤:一是放置在合适的目录下,二是将它启动起来。

launchd 的配置文件目录

系统给 launchd 分配了数个目录,根据目录的不同,服务生效的时机和范围也不尽相同。分别是:

  1. ~/Library/LaunchAgents
  2. /Library/LaunchAgents
  3. /Library/LaunchDaemons
  4. /System/Library/LaunchAgents
  5. /System/Library/LaunchDaemons

其中,/System 目录下的文件是由系统自身管理的,需要 root 权限才能编辑,尽量不要改动,避免意料之外的错误。所以我们主要把 .plist 文件放置在前面 3 个目录下,取决于我们希望其启动的时机:

  1. ~/Library/LaunchAgents,仅在当前系统用户登录以后生效
  2. /Library/LaunchAgents,在任意系统用户登录以后生效
  3. /Library/LaunchDaemons,在系统启动完成时(登录界面)生效

在我们的实例当中,放在 ~/Library/LaunchAgents 目录下就可以满足需求了。 在 Finder 中,使用 ⌘Command-⇧Shift-G 快捷键呼出「前往文件夹」弹窗,键入路径以后就能直接访问了。

在 Finder 里打开 LaunchAgents 文件夹

把刚刚编写好的配置文件拖到这个文件夹就可以了,同时确保配置文件指向的脚本也已经就位。

使用 launchctl 来启用 launchd 配置文件

准备就绪以后,我们还需要让系统加载这个配置文件,需要使用「终端」(Terminal)执行 launchctl 命令:

~ > launchctl load ~ /Library/LaunchAgents/slack.archivebot

~ > launchctl start slack.archivebot

只要没有报错,就已经正确运行了,你可以用 list 命令查看所有运行中的 launchd 服务:

~ > launchctl list

如果一个服务需要停用,同样也要执行两条命令,这个命令也可以作用在其它服务上,效果相当于你在 CleanMyMac 里将某个服务禁用:

~ > launchctl unload ~ /Library/LaunchAgents/slack.archivebot

~ > launchctl stop slack.archivebot

到这里,我们的实例就讲解完了,这只是 launchd 服务最基本的一种用法,给一个自定义的服务保活。实际上,launchd 可以调用和配置的属性非常之多,短短一篇文章是没有办法说完的,如果你想把它用好,可以看看苹果官方的手册或者是其它玩家整理的教程。我会把相关链接贴在最后。

小结

本文给大家介绍了 macOS 里自启动项目的主要方式,登录项与启动代理。如果只是想要管理它们,在系统偏好设置,或者 launchctl 命令就可以了。如果你有 CleanMyMac,也可以在图形界面上很容易地操作。

通过一些简单的调整,你就可以把不必要的启动项目和常驻项目移除,提升系统的启动速度,减少运行过程中的资源占用。

另外,这些启动的机制同样可以为我们所利用,可以把自己想要的启动项目,无论是应用程序还是服务、自动化脚本添加到启动项目里常驻运行,这个需要借助系统服务 launchd 的力量,也会困难一些。

希望能看到有人借助 launchd 开发出一些独特的玩法,然后再分享给其他玩家。

最后,给对本期选题感兴趣的朋友留个小问题:

其实 Mac 还有一种自启动的机制,名为 StartupItems,同样是把 .plist 文件放置在对应的目录下来执行。你可以自己研究一下它与本文介绍的登录项和启动代理的区别,可以找到它对应的目录,同时看看里边有些什么,它对应的又是什么机制呢?欢迎在留言里告诉我你的发现。

注:本文选题由 @Hum 提出,题图由 @咕毛 绘制。

参考阅读