如何优雅地更新 MACOS 系统上的软件

上一次更新正好是一年前,所以更新一下。
既然是为了更新而更新,就来写写如何更新吧 ;)
TL;DR:直接看「更优雅地更新软件」小节。

关于 macOS 上的软件安装

与 iOS 正常情况下只能通过 App Store 安装 APP 不同,macOS 有类似 Windows 的自由度来安装和使用第三方软件。
在「设置」-「隐私与安全性」-「安全性」-「允许以下来源的应用程序」中开启「App Store 与已知开发者」后,无论是通过 Homebrew 还是软件官网下载,只要第三方软件满足以下两点即可直接使用:

  • 有效代码签名(Developer ID 证书签名)

开发者加入 99 刀/年的 Apple Developer Program 来获取 Developer ID 证书。

  • 苹果公证(Notarization)

开发者把签好名的 App 上传到 Apple Notary Service,苹果自动扫描通过后打上公证凭证。

关于在 macOS 上更新软件

安装后,对于新版强迫症患者最大的问题是 macOS 上的更新途径非常分散且不够优雅:

使用 App Store 更新

App Store 可以更新通过 App Store 安装的软件,但如果一个软件只在国区上架,美区商店的接口中就没有这个软件的安装包和分发资源,且 App Store 的购买凭证会永久绑定下载时的账号,更新时凭证与账号不匹配也会报错。
定期切换 App Store 上登陆的账号更新一波是一种解决办法,但因为 App Store 和「音乐」、「播客」、「视频」等共用同一个媒体与购买凭证,在 App Store 注销后这些软件也会同步被登出,所有的离线缓存歌曲、视频被清空、资料库重置,一不小心还可能触发了苹果的「90 天设备绑定限制」。
避免这些的办法是使用 macOS 的多用户切换,在「系统设置」-「用户与群组」-「添加用户」里创建一个本地账户,然后在这个账户中登陆国区 ID,用这个国区 ID 安装国区独占软件,每次要更新的时候切换一次用户。

使用 mas 更新

mas 可以用 Homebrew 的 brew install mas 来安装,然后用 mas upgrade 来更新通过 App Store 安装的软件,mas 本身的升级非常的…呃…突飞猛进?
mas 的问题是苹果移除了一些私有 API 后不能再使用 mas signin 自动切换账号或区域了,只能完全依赖当前 App Store 会话所在的区域,用 mas outdated 可以检测到本地已下载软件有更新版本,但对独占软件执行 mas upgrade 时会失败。
mas 本质上是 macOS 私有框架的命令行封装工具,下载、验证和安装请求全部交给 App Store 的后台守护进程,所以更新限制也和用 App Store 更新一样。

使用 Homebrew 更新

Homebrew 可以用终端命令 brew update && brew upgrade 或 BrewUI 来更新自己和通过 Homebrew 安装的软件,比如上面的「mas」。
通过 Homebrew 进行软件更新的问题在于 brew 实际只检查版本号大小,不在乎软件本身的许可授权,所以会出现某个软件从 1.X 更新到 2.X 后才发现许可失效,Homebrew 又没有回滚命令,要不重新购买,要不就重新安装低版本……
另外,Homebrew 判断软件过时用的是两份数据——远端元数据和本地安装记录,如果用非 Homebrew GUI 工具绕过 brew 来更新则 Homebrew 本地记录不会同步,于是 brew outdated 会重复列出,brew upgrade 会把最新版重新安装,即使本机已更新好最新版。

使用软件自带更新

软件自带更新需要软件保持前台打开或在后台运行,而有些软件可能很久都不会打开却想在用到时是最新版本。

一些尝试

Homebrew & mas 一把梭

Homebrew 的官方 GUI BrewUI 目前的版本只是把 Brew 命令给图形化,和 Homebrew 的第三方 GUI 如 Cork 一样不支持 mas 和 Sparkle ,所以使用命令行一把梭反而更简单。
我一直使用很多教程里提到的:

brew update && brew upgrade && brew cleanup && mas upgrade

直到写本文时让 AI 检查才发现这条命令可以优化:

  • && 表示“上一条成功才执行下一条”,前面一步骤报错会自动停下,可以改用分号 ; 分隔,分号代表无论前一步是否报错都强行往下跑;
  • 如果没有在环境变量中设置 HOMEBREW_NO_AUTO_UPDATE=1 ,执行brew upgrade 默认会自动 brew update 更新自身并刷新数据,所以可以直接去掉 brew update;
  • brew upgrade 默认不会更新那些声明了 auto_updates true 内置更新机制的 Cask 软件,这其实是一种保护措施,但管它的,都一把梭了 ;)加上 --greedy-auto-updates 参数来强制更新;
  • 给 brew cleanup 加上-s 来深度清理下载缓存,顺带加上 brew autoremove 清理不再使用的依赖。

优化后的命令:

brew upgrade --greedy-auto-updates; brew autoremove; brew cleanup -s; mas upgrade

为了方便使用,可以把它写成 alias 别名或 Zsh 函数:
使用 alias 别名:

Alias 即别名是 Shell(如 Zsh、Bash)中的命令快捷方式,本质上是文本替换机制:用一个简短易记的词代替一段冗长、复杂的完整命令,只能直接把参数拼在末尾,无法写条件循环。

在终端输入:

echo "alias upall='brew upgrade --greedy-auto-updates; brew autoremove; brew cleanup -s; mas upgrade'" >> ~/.zshrc
source ~/.zshrc

使用 zsh 函数:

zsh 函数(Zsh Function)是在 Z shell 中将一段或多段命令组合并命名封装的代码块。定义后,这个名称就会像一个自定义命令一样,直接在终端里输入并执行,支持多参数提取、分支判断与循环控制。

首先在终端用 nano 编辑器打开 ~/.zshrc 配置文件

nano ~/.zshrc

把下面的函数粘贴进去:

upall() {
    echo "==== [1/5] 正在更新 Homebrew 命令行工具 (Formulae)... ===="
    brew upgrade --formula

    echo "\n==== [2/5] 正在检查并更新 Homebrew 桌面应用 (Cask)... ===="
    # 获取需要更新的 Cask 列表
    local casks=($(brew outdated --cask --greedy-auto-updates | awk '{print $1}'))
    local total=${#casks[@]}

    if [ $total -eq 0 ]; then
        echo "所有 Homebrew Cask 软件均为最新版本。"
    else
        echo "发现 $total 个可更新 Homebrew Cask 应用,开始升级..."
        local count=1
        for app in "${casks[@]}"; do
            echo "\n==> [$count/$total] 正在升级: $app"
            brew upgrade --cask "$app"
            ((count++))
        done
    fi

    echo "\n==== [3/5] 清理孤立依赖包... ===="
    brew autoremove

    echo "\n==== [4/5] 深度清理 Homebrew 缓存与旧版本... ===="
    brew cleanup -s

    echo "\n==== [5/5] 正在更新 Mac App Store 应用... ===="
    mas upgrade

    echo "\n🎉 恭喜,软件更新完毕!"
}

按下 Ctrl + O 后回车保存,然后按下 Ctrl + X 退出 nano 编辑器,接着重载后生效:

source ~/.zshrc

alias 别名或 zsh 函数配置好后之后每次更新只要在终端输入 upall 就能一把梭了,如果同时设置别名和函数记得给其中一个改个名。

使用 Topgrade 一键更新

Topgrade 会自动检测 macOS 系统里的所有包管理器和软件更新机制,比如 Homebrew、mas,然后按顺序「一键更新一切」。
安装 Topgrade:

brew install topgrade

之后在终端输入 topgrade 一键更新,不过推荐先用 topgrade -n 预览一下,因为「一键更新一切」是优点也是缺点:

  • 默认扫描面过广

更新全局 Python/Node 包容易引发全局 CLI 工具依赖冲突,甚至破坏某些基于特定解释器版本的本地脚本;

  • 默认包含 softwareupdate 导致执行非常慢

macOS 自带的系统更新命令行 softwareupdate 握手和检测十分迟缓,甚至常在终端挂起数分钟。如果不在配置中 disable = ["system"] ,维护耗时会大幅增加;

  • 不能解锁跨区限制

作为命令行工具,Mac App Store 跨区无法更新问题 Topgrade 没法解决;

  • 无法实时检测

当然可以用定时任务,但需要添加 --yes 来跳过交互式,而且 sudo 不能无人值守。
埃尼喂,不如使用上面的 alias 别名或 zsh 函数一把梭。

使用 Pearcleaner 更新

Pearcleaner 是 Alin Lupascu 受 AppCleaner 和 Sun Knudsen's Privacy Guides 启发开发的,说实话我试过后就卸载了 AppCleaner LOL
Pearcleaner 集成了:

  • 类似 AppCleaner 的「应用程序管理」
  • 通过删除程序二进制文件无用架构来减小体积的「程序瘦身」
  • 针对常见 IDE 的「清理开发环境」
  • 带高级筛选器的「文件搜索」
  • 管理和维护 brew 的「Homebrew 管理器」
  • 侦测已卸载程序残留的「孤立文件清理」
  • 管理 macOS 第三方插件和拓展的「插件管理器」
  • 管理启动项的「启动服务管理器」
  • 支持在同一个界面里升级 App Store、Homebrew、Sparkle 三个更新渠道的「更新应用程序」。

我非常喜欢它的「更新应用程序」功能,当然其它功能也非常好用,甚至比很多专用收费软件更好,所以在很长的一段时间里 Pearcleaner 是我的首选软件管理器,可惜它从 2025 年后已处于「On Hold」状态:

As you may have noticed, development on the app has basically stopped since end of 2025…
… issue responses, feature work, PR reviews, and new releases are effectively on hold indefinitely for now.

Hold 后还可以继续用,但目前已经有一些小 Bug 了,比如读取 Sparkle 渠道的更新日志时会假死,还有和 Homebrew 的联动也时不时出错或失效……

使用 Mole For Mac 更新

在几次使用 Pearcleaner 遇到问题后,我购买了 @Tw93 开发的 Mole For Mac AKA 「鼴」,它的 CLI 版一经发布就好评如潮,目前已经超过 65K Star。
Mole For Mac 和 Pearcleaner 一样是从 macOS 清理软件做起,后加入软件更新功能,它们的逻辑和问题也和 Homebrew 一样:“只检查版本号大小,不在乎软件本身的许可授权”。
BTW,推荐一下 Mole 中文博客,写得非常好,比如「如何更新 Mac 上的所有应用」:

mole 会列出应用来源,通过对应渠道检查更新,阅读兼容说明并备份关键状态,再分批安装,每批完成后,用原来的文档和工作流复测,无需一次把所有版本号变成最新,先保住可用环境。
为什么 macOS 没有一个能更新所有应用的按钮?
App Store 依靠 Apple 收据管理软件,Sparkle 使用开发者发布的 appcast,配置正确时会同时校验 EdDSA 签名和 Apple 代码签名,具体机制见 Sparkle 的安全说明,Electron 应用和厂商更新器使用各自的订阅源,Homebrew 则按 formula 与 cask 升级。

更优雅地更新软件

使用 Duo Updater 更新

Duo Updater 是 DuoDuo AKA jizhi0v0 在 GitHub 上开源的软件更新器,可以看看开发者自己写的介绍:Duo Updater:按每个 app 自己的方式更新它 ,另外使用前推荐读一下 Duo Updater 的文档,里面有 Duo Updater「以什么逻辑工作」、「更新软件的安全策略」、「索取的权限和作用」还有「软件及其官网的隐私策略」。
目前 Duo Updater 支持的渠道不如 Topgrade 广泛但已足够,根据以下顺序获取更新:

  • Mac App Store

使用苹果的 iTunes 查询 API 。仅信任原生 macOS 软件结果,iOS-on-Mac 应用会被跳过,因为它们的版本号独立变动,否则会显示为无法安装的更新。

  • Xcode 版本库

非来自 App Store 的 Xcode 构建版本:每个测试版和候选发布版,与实际安装的渠道匹配。已通过 App Store 安装的 Xcode 已在上文说明。

  • Sparkle

使用应用自身的 SUFeedURL 应用源(appcast),与应用内置更新器读取的源相同。

  • Homebrew Cask

通过 .app 文件名匹配,若无法匹配则回退至 bundle ID,因此即使安装 pkg 而非应用包的 cask 也能被找到。

  • GitHub Releases

对以此方式分发的应用进行渠道感知匹配。仅用于检测,除非某应用有特定规则已命名并验证过可安装的 macOS 资源。

使用 Alcove 许可证访问的私有更新。

  • 供应商探测

针对未发布任何源或商店列表的供应商端点,手动编写的规则。
本文标题是「如何优雅地更新 macOS 系统上的软件」,Duo Updater 优雅在:

MacUpdater 停更之后,我写了个 macOS 菜单栏更新器:不把所有 app 塞进同一条安装管线,而是顺着每个 app 本来的更新方式去装。

这也是 Duo Updater 和 Pearcleaner 不一样的地方,Pearcleaner 会获取通过 Homebrew 安装的软件更新并列出(类似 brew ls ),还会获取通过 Sparkle 发布的更新并列出,而有些软件会同时在这两个渠道存在——导致重复列出。
Duo Updater 的做法是:

顺着来,不硬来
大多数更新器会挑一条机制,把所有 app 都塞进去。这个不。它先看这个 app 本来是怎么更新的——有的自带更新功能,有的从 App Store 装,有的是用 Homebrew 装的——然后就走那条路。app 自己能更新好的,它交出去让人家自己来;有什么事它做不安全,就直说,而不是猜着来。
界面上尽量把话说清楚:每一行写明你要从哪个版本走到哪个版本,按钮上写的就是接下来真正会发生的事。Update 是装,Relaunch 表示新版本其实已经装好了、只是开着的那份还是旧的。绿点表示这个 app 正在运行,你点之前就知道会不会有东西被关掉重开。
安装这块有几条不肯让步的规矩:绝不强行关掉正在运行的 app——该弹的保存提示照弹,你可以取消,取消了它就不动;大版本升级会先提醒一声再问你,而不是一键装完才发现要重新买授权。装之前还会留一份备份,出问题能退回去。

比如对于 App Store 安装的软件,Duo Updater 可以通过 mas 和特权助手无障碍跨区域更新(伪造 Storefront 请求与多凭据代理?),且正在运行的软件会暂停并提示打开 App Store 后继续,而不是直接关闭。
如果更新 App Store 安装的软件失败也会给出下一步:

对于自带更新的软件,在 Duo Updater 设置里选择「运行时延后」,正在运行的软件只会通知不会直接更新:

此外 Duo Updater 的「更新日志」查看功能和「发布日志」附带的热力图也很赞,但我最喜欢的是它的 AI 感,看看它自己的「更新日志」和 GitHub 页面你会明白我的意思:

Duo Updater 的更新日志
Duo Updater 的更新日志

Duo Updater 项目的 Issues 页面,AI 自己在玩 LOL
Duo Updater 项目的 Issues 页面,AI 自己在玩 LOL

Duo Updater 的问题是目前的版本没有处理和 Homebrew 的联动,本地 Homebrew 数据库不会在软件被 Duo Updater 更新后更新,导致运行 brew upgrade 时已经更新过的软件被重复更新。 update 2026/09/24 ,预计于 Duo Updater v0.4.4 版解决:

用 Homebrew 安装的应用,现在通过 Homebrew 更新。 这针对的是由 brew 安装、同时又自带更新器的应用。以前 DuoUpdater 会直接原地更新它们,Homebrew 并不知道,于是下一次 brew upgrade 会把你已经装好的版本再下载、再装一遍。

当前该版本未实装,因此不知道会如何处理 Homebrew Cask 更新很多时候会晚于 Sparkle 渠道的问题。

One More Things…

既然提到了「更新日志」,再推荐一个最近发现的好软件:Mac Performance Monitor 。

Mac Performance Monitor 是 macOS 原生的性能分析器与记录器,常驻菜单栏。
它持续记录 CPU、内存压力、GPU、网络、磁盘、电池及各进程的使用情况至本地数据库,并帮助你分析数据:趋势、内存泄漏、压力事件及设备诊断。
完全免费且开源。无遥测功能。所有采样数据均存储在你的 Mac 上。

Mac Performance Monitor 和 iStat Menus 一样记录并展示数据,不一样的是它更详细且提供了继续使用数据的方式:

  • 实时提醒进程异常,比如疑似内存泄漏或大量网络活动;
  • 自定义的数据保存,可调整颗粒度,方便给 AI 或自己事后分析。

Mac Performance Monitor 甚至会提醒自身 LOL
Mac Performance Monitor 甚至会提醒自身 LOL

自从发现 Mac Performance Monitor 后我就删除了 iStat Menus 也取消了 mole 常驻菜单栏……
刚好今天 Mac Performance Monitor 通过了 Homebrew 的审核,可以一键安装了:

brew install --cask mac-performance-monitor

Mac Performance Monitor 的 Changelog 遵循「如何维护更新日志 」和「语义化版本」,看起来就赏心悦目 LOL

Mac Performance Monitor 的更新日志
Mac Performance Monitor 的更新日志

不过 Mac Performance Monitor 没有在 Sparkle appcast 中定义更新日志日志,所以目前 Duo Updater 上不能读取。 给 Duo Updater 发了 issues,大概两三个小时后就搞定了……Orz

Comments
Write a Comment
  • Marco reply

    duo updater试了下挺好用的!谢谢