简介:Homebrew 是 macOS 上广受欢迎的开源软件包管理工具,面向开发者、运维人员及需要高效管理命令行工具与图形应用的 Mac 用户,解决软件安装、更新、卸载与依赖管理难题。压缩包共 2000 个文件,包含大量 Ruby 脚本、Markdown 说明文档、YAML/JSON 配置文件、shell 辅助脚本及二进制可执行程序,整体约 421.56MB,可支撑离线查阅与二次开发。已有 21758 人学习下载,热度较高。包内系统呈现了 Homebrew 的安装脚本、常用 brew 命令、Cask 图形应用管理、自定义 tap 机制、维护清理与问题排错策略,并收录众多软件包定义与依赖信息,适合想深入理解 Homebrew 内部结构、搭建定制化包管理环境或进行离线部署的读者参考。
1. Homebrew 软件管理工具到底是什么:从一次手动安装事故说起
在我用 Homebrew 之前,最怕的一类事是“装个小工具要配一整套依赖”。比如有次我在 macOS 上想装个命令行解析工具,curl 下源码,make 的时候告诉我缺了一个编译库;我去找这个库的安装包,又发现它依赖另一个更老的版本。来回折腾了一上午,最后还是别人用 Homebrew 一条命令装完。Homebrew 是 macOS(也支持 Linux)上被广泛使用的软件包管理工具,它解决的不只是“下载”,而是依赖解析、安装路径、版本切换和卸载清理这一整条链路。这篇文章写给两类人:一是刚接触命令行的新手,想安全地把开发环境搭起来;二是被重复编译和乱装的旧工具折腾过的熟手,想弄清 Homebrew 的边界和踩坑点。
2. Homebrew 的工作原理与最小安装:目录、依赖解析与镜像源切换
2.1 目录结构与软链接:为什么依赖能安安分分待在 Cellar 里
在理解 Homebrew 之前要先理解它的安装前缀。在 Intel 芯片的老 Mac 上,Homebrew 默认装在 /usr/local;从 M1 开始,Apple Silicon 的 Mac 默认装在 /opt/homebrew。这里有个设计前提:Homebrew 会把软件装进自己的目录,再通过软链接把命令暴露到 PATH 里。这样做的好处是,它不会像 dmg 安装包那样把文件散落到系统目录。
/opt/homebrew/Cellar 是“地窖”,每个软件以及它的每个版本都在 Cellar 下占一个子目录,比如 Cellar/nginx/1.25.3/bin/nginx,是二进制真实位置;而 /opt/homebrew/bin 里放的是 Cellar 里那些文件的符号链接。这样可以让多个版本并存,想切换版本时改一下链接指向,用户看到的还是同一个命令入口。手动编译过的老玩家应该懂这个价值:以前用源码编译,装一个库就要手工配置 --with-xxx,权限混乱和路径错位几乎是必然的,而在 Homebrew 的前缀下,依赖都锁在同一个 Cellar 里,互不干扰。
依赖解析是它真正的内核。brew install 一条命令执行时,会先读取 Formula(一个 Ruby 脚本)定义的信息,看看软件依赖哪些库,然后按拓扑顺序把它们一组装下;已经装过且版本满足的依赖会被跳过。依赖关系不是手写的,而是 Homebrew 根据公式自动构建依赖树。再配合官方预编译的二进制包(bottle),与系统环境匹配时直接解压软链接,只有架构不匹配或你强制源码编译时才走本地编译。理解这一点,后面遇到“安装很慢、编到一半报错”时,第一反应就是“是不是没能用上 bottle”,而不是怀疑代码本身有问题。
Homebrew 安装后有几个目录值得提前记住:
| 路径 | 作用 | 什么时候手动动它 |
|---|---|---|
| /opt/homebrew/Cellar | 存放已装软件本体与版本 | 一般不动,卸载由 brew uninstall 处理 |
| /opt/homebrew/bin | 命令符号链接 | 只有 PATH 冲突时才手动检查 |
| /opt/homebrew/var | 部分软件的数据文件 | mysql、postgres 数据目录,备份前先看这里 |
| /opt/homebrew/Library/Taps | 第三方公式仓库 | 安装 tap 后自动添加 |
| /opt/homebrew/Caskroom | dmg 类图形应用 | 不要手动删除 |
2.2 安装 Homebrew 的最小命令与国内镜像源切换
在新 Mac 上装 Homebrew,前提是先有 Xcode Command Line Tools,这是一组命令行编译工具。没有它,安装脚本会中途退出并明确提示。手动装的话,打开终端执行 xcode-select --install,装完后再继续。注意,Homebrew 官方安装脚本本身不会帮你装 Command Line Tools,它只会提示你缺什么。
最小安装命令是这个,但官方脚本地址经常被网络环境卡住,常见做法是先配国内镜像再执行:
export HOMEBREW_BREW_GIT_REMOTE="https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git" export HOMEBREW_CORE_GIT_REMOTE="https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git" export HOMEBREW_BOTTLE_DOMAIN="https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles" /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"逻辑说明:前三行把 Homebrew 自身的 Git 仓库、核心公式仓库、预编译二进制包三个下载源的地址都指向国内镜像。第四行才是真正执行官方安装脚本。较新版本的 Homebrew 已经开始用 HOMEBREW_API_DOMAIN 代替 HOMEBREW_CORE_GIT_REMOTE,所以如果发现某些公式拉不下来,再补一条 HOMEBREW_API_DOMAIN 指向镜像的 api 地址即可。
参数说明:HOMEBREW_BOTTLE_DOMAIN 是最关键的一个,决定二进制包的下载源,设了它编译时间能从小时级降到分钟级;HOMEBREW_BREW_GIT_REMOTE 决定 brew 自身的更新源。如果你的终端里已经有旧的 GitHub 相关环境变量残留,先 unset 掉对应变量再执行,避免混用。
装完以后做两层校验:
brew --version brew doctorbrew --version 看版本号,确认命令可用;brew doctor 会逐项检查环境问题。如果它输出一堆 warning,别慌,其中一部分是“目录名大小写”这类不影响使用的问题。真正常见问题是 Python、Ruby 版本冲突和 PATH 指向异常,看输出里有没有 Error 字样,没有就可以继续。
注意:Apple Silicon 机器安装脚本会自动选择 /opt/homebrew,Intel 机器则是 /usr/local。如果你之前在 Intel 迁移过数据,确认自己的 brew 到底在哪个前缀下,两个前缀同时存在会带来双份 PATH 冲突。
3. Homebrew 基本操作:查询、安装、卸载与服务管理的常用 brew 命令
3.1 查询与安装:brew search、brew info、brew install 的常用参数
拿到 Homebrew 之后第一步是学会查,不要去猜包名。brew search 支持模糊匹配,brew info 能告诉你这个包的版本、依赖、安装后注意事项。比如我想装 jq,通常会这样:
brew search jq brew info jq brew install jqsearch 会输出关键词能匹配到的公式和 cask,info 会显示当前是否已装、最新版本、依赖了哪些库、安装时有哪些 caveats。第三行才是真正安装。默认情况下,如果官方给当前系统架构提供了预编译 bottle,安装就是下载解压加软链接,几十秒就完事。如果 info 里写着依赖 Xcode Command Line Tools,说明这个包编译时要用到系统编译器。
brew install 有几个参数我会在不同场景下用:
- --dry-run:只预览将要安装哪些依赖而不真正写盘,适合大包安装前评估风险;
- --build-from-source:强制源码编译,适合你要改 Formula 或测试兼容性时用;
- --force:重复安装,比如已经装了低版本想强制重装;
- --ignore-dependencies:绕过依赖检查,只在排查依赖冲突时用,正常不建议。
新手不建议上来就加一堆参数,默认安装路径最省心。等你需要自定义编译选项时再回来研究这些参数不迟。
3.2 卸载与清理:brew uninstall、brew list 与 brew autoremove 的使用边界
查询已装包我一般用 brew list,作用类似 rpm -qa 或 dpkg -l。加 --versions 参数可以在列出包的同时显示版本号。要卸载某个包用 brew uninstall,它与手工 rm 的区别是会处理软链接和依赖记录。
brew list brew list --versions brew uninstall nginx brew autoremovebrew list 不带参数会列出所有已装公式;加 --versions 可以知道本机每个工具处于哪个版本。brew uninstall 只卸载你指定的那个包,不会自动清理那些已经不再被任何包依赖的遗留依赖,这需要 brew autoremove 来做。autoremove 会把“不再被依赖”的包一次性清掉,清之前可以用 --dry-run 先看看它会删哪些东西,避免误删你还想留着的工具。
3.3 后台服务:brew services 管住 mysql、nginx 这类常驻进程
装完 nginx、mysql、postgresql 这类需要常驻的服务之后,直接敲命令是前台运行,终端一关就停了。更可靠的做法是用 brew services 管理:
brew services start mysql brew services stop mysql brew services liststart 会把服务注册成后台常驻进程,并设置开机自启;stop 停止并取消自启;list 可以查看当前已注册服务的运行状态。如果你只是想在终端前台跑一次并观察日志,可以用 brew services run,它不会注册开机自启。这里有个细节:在 macOS 上 brew services 底层用的是 launchd,生成的配置文件在 ~/Library/LaunchAgents 下,如果你有更复杂的守护需求,可以直接去改那份配置,不一定要依赖 brew services 的子命令。
4. Homebrew 升级与依赖维护:brew update、upgrade、cleanup 的配合与版本锁定
4.1 分清 brew update 和 brew upgrade,别把升级搞成大翻车
我见过太多人把这两个命令混着用。brew update 拉取 Homebrew 自身和 tap 仓库的新索引,它不会动你已经安装的软件。当你发现 search 一个刚发布的新包搜不到时,跑 update 就能解决。brew upgrade 则是真正把已安装的软件升级到新版本。
正确顺序是先 update,再 outdated 看有哪些包有可用更新,最后决定是整批升级还是挑几个包升级。我在这台开发机上一般只升级自己明确需要的,比如 build 工具和语言运行时;数据库、nginx 这类包如果没有安全更新需求就放着。
brew update brew outdated brew upgrade如果只想升级某个包,把包名跟在 upgrade 后面,比如 brew upgrade nginx。brew outdated 的输出里会带“当前版本 -> 新版本”的对照,能在升级前看到跳级幅度。有些大版本跳级会改配置格式,比如 PHP 或者 OpenSSL,升级完才发现旧配置起不来是很常见的。
4.2 升级前记录版本、升级后跑 brew doctor 的维护流程
升级前建议留个“后悔药”:输出当前所有已装包的版本号,保存成文件,升级后 diff 就知道谁变了。再配合 brew doctor 检查环境。
brew list --versions > ~/brew_versions_$(date +%F).txt brew upgrade brew doctor第一行把版本快照写到家目录,文件名带日期,一天多次执行不会覆盖。第二行整批升级。第三行在升级后跑一遍,确认软链接、路径、权限有没有出错。升级过程中如果某个包编译失败,日志在 ~/Library/Logs/Homebrew/<公式名>/ 里,先看失败步骤再决定是重装还是等新版。升级完先别急着 cleanup,旧版本清了就回不去了,等新版本用几天确认没问题再清理不迟。
4.3 用 brew pin 锁定不该升级的包,给生产环境留一条后悔药
有些包升级影响面很大,比如 OpenSSL 这种被大量包依赖的底层库,或者你在跑自家产品依赖的运行时。Homebrew 提供 pin 机制:
brew pin mysql brew unpin mysql brew list --pinnedpin 之后 brew upgrade 会跳过它;需要手动升级时先 unpin 再升级。这是维护生产环境时最常用的小手段。依赖树越复杂,越要珍惜 pin 的作用——Upgrade 时看似只升了一个包,实际可能连锁触发一串依赖更新,这种失控感是最让人头疼的。把关键服务锁住,至少保证每天早上不会被莫名其妙的依赖变更打乱节奏。
5. mac 安装 Homebrew 失败、卸载残留与权限错乱:一份排错避坑手册
5.1 mac 安装 Homebrew 失败的三种经典报错与解决路径
第一条是 Command Line Tools 缺失。现象是安装脚本执行后不久就退出,提示开发者工具相关错误。原因:新机器或重装系统后没有装命令行工具。解决:执行 xcode-select --install,安装完成后再跑官方脚本。有时代理会提示 already installed,但实际路径不对,这时用 sudo xcode-select --switch /Library/Developer/CommandLineTools 修正路径。
第二条是网络导致的 git clone 或 curl 超时。现象是出现 Failed to connect 或 Operation timed out 这类输出。原因:GitHub 访问不稳定,或者内网环境有额外限制。解决:回到第 2.2 节的镜像源方案,那比在失败日志里反复重试要有效得多。
第三条是旧系统支持收紧。现象比较明确:运行安装脚本直接出现 Unsupported macOS version 之类的提示,常见于 macOS 10.15 及以下系统。原因:Homebrew 近期版本放弃了对旧系统的兼容,新公式和预编译 bottle 也更多面向新系统。解决:不升级系统的情况下不要硬装新版,去找历史版本安装脚本,把 brew 本体锁在旧版本,同时放弃新公式支持;更长远的方案是换 MacPorts。别指望官方继续维护旧系统,这不是临时 bug,而是方向性调整。
5.2 Homebrew 卸载残留导致重装失败:清锁文件、清缓存目录
现象:手动删了 /opt/homebrew 或用卸载脚本卸载后,还没装新版本,再次执行安装脚本时报 Another active Homebrew process is already running,或者重装完后发现旧配置文件仍然存在。原因:Homebrew 的进程锁和状态文件留在了原前缀目录里,而且它的缓存主要落在用户 Library 下,并不全在 /opt/homebrew。解决:先 pkill 掉仍在跑的 brew 相关进程,软件服务用 brew services stop 全部停掉;再删除残留目录 /opt/homebrew 或 /usr/local,同时清掉缓存 ~/Library/Caches/Homebrew 和日志 ~/Library/Logs/Homebrew。
如果只是想解决锁问题,精确做法是删除前缀目录下的 /opt/homebrew/var/homebrew/locks 里的文件,然后重新安装。注意:重装成功后,旧版本软件的配置文件一般不会自动消失,它存在 /opt/homebrew/etc 或 ~/Library/Preferences 里。这是 Homebrew 的“善意”设计:卸载不动配置文件,但如果你确定不再需要旧配置,记得手动清,否则下次装同一个软件会发现配置还在,可能对也可能错。
5.3 权限错乱与依赖冲突:不要一上来就 sudo chown
现象:brew install 执行到一半报 The following directories are not writable by your user,或者装完运行某命令时提示 Permission denied。原因:绝大多数情况是曾经用 sudo 运行过 brew install 或 brew update,导致前缀目录下文件属主变成了 root。解决思路分两步走。先确认是不是只有这一个问题:ls -ld /opt/homebrew 查看属主;如果属主不是当前用户,再执行:
sudo chown -R $(whoami) /opt/homebrew但这一步要非常克制:chown -R 会把下面所有文件属主都改成你,如果这台机器有多个用户共用,别人的数据会遭殃。我一般把它作为最后手段而不是第一反应。更安全的流程是导出 brew list --versions,卸载 Homebrew,重装,再按清单装回来。虽然多花时间,但能避免权限连带问题。
依赖冲突的另一个常见剧本:装了 pyenv 又 brew install python,两条路径都能提供同一个命令,PATH 顺序不同导致你调到的不是想要的那个版本。此时不要直接删目录,先用 brew list --versions 和 which python 确认到底谁在生效,再通过调整 PATH 或 brew link --overwrite 解决。这种问题说白了是“同一个命令有多个来源”,跟 Homebrew 本身关系不大,但它经常成为评估 Homebrew 可靠性的替罪羊。
6. 把自己写的工具做成 Homebrew 公式:一条装进团队机器的进阶路径
如果你有自己写的命令行工具、公司内网脚本,分发是一个绕不开的麻烦。常见做法是把它们做成一个 Homebrew 的 Tap 仓库:只要是 GitHub 上一个名为 homebrew-xxx 的仓库,用户执行 brew tap your-name/your-repo 后,仓库里 Formula 目录下的公式就能被 install。这比让同事去下载 zip 解压省心得多,依赖自动解析、版本更新也由 Homebrew 统一管。
一个最小的 Formula 是这样:
class Mycli < Formula desc "Just for internal use" homepage "https://your-team.example.com" url "https://your-team.example.com/releases/mycli-1.0.0.tar.gz" sha256 "这里填 shasum -a 256 计算出的校验值" version "1.0.0" depends_on "cmake" => :build def install system "make", "PREFIX=#{prefix}" bin.install "mycli" end end逻辑说明:class 名必须与公式文件名大小写对应,Mycli 对应 mycli.rb;url 指向 release 压缩包;sha256 是必须写对的一步,写错会直接报错;def install 定义安装动作,这里是把 make 产出的 mycli 可执行文件放进 bin 目录并做软链接。使用者侧只有两行:
brew tap your-name/your-repo brew install mycli本机验证时不用推远程,直接本地安装公式文件:brew install --build-from-source ./Formula/mycli.rb,能过就说明公式没写错。以后更新版本只要改 url、version、sha256,打上 git tag。如果你的目录同时有 cask 需求,把图形应用放在 Cask 目录里,适用于 .dmg / .pkg 的分发,这部分跟 Formula 是两套规则。
我印象里最常踩的坑是自己写公式时一句话没写 depends_on,结果某台机器从源码编译失败,因为那台机器缺少对应编译器。从那之后,我每提交一个公式都会先本地跑一遍 --build-from-source,再在干净环境里验证一次。养成这个习惯之后,被同事找上门说“装不上”的次数少了很多。希望帮到你。
本文还有配套的精品资源,点击获取