Homebrew 是 macOS 上最流行的包管理器,但随着日常使用中不断安装各种工具和库,系统中难免会积累大量不再需要的依赖包。本文将系统性地介绍如何查看依赖关系、理解包的分类与安装机制,并提供一套完整的清理策略,帮助你维护一个干净高效的开发环境。

提示:本文由 AI 根据对话历史整理,仅供参考

# 查看依赖关系的常用命令

在清理之前,首先需要理清包与包之间的依赖关系。Homebrew 提供了几个非常实用的命令来完成这项工作。

# 树状查看所有已安装包的依赖

brew deps --installed --tree

以树状图形式列出所有已安装的 Formula 及其各自的依赖,适合全局概览整个依赖结构。

# 查看特定包的依赖

brew deps node --tree

只展示指定包(如 node)的依赖树,适合分析某个包的"下游"依赖。

# 反向查询:谁依赖了某个包

在删除某个包之前,务必确认没有其他软件在使用它:

brew uses --installed openssl@3

如果返回了其他软件的名字,说明该包正在被依赖,不能贸然删除。

# 查看顶层包(叶子节点)

brew leaves 会列出所有不被其他包依赖的"叶子节点"包,这些通常是你手动安装的目标软件:

brew leaves

如果你想进一步查看这些包的用途说明:

brew leaves | xargs brew desc --installed

# 查看手动安装的包

使用 brew bundle dump 可以导出你主动安装的 Formula 和 Cask,不包含因依赖关系自动安装的包:

brew bundle dump --file=-

这个命令也非常适合在换机时备份你的软件清单,之后只需运行 brew bundle 即可一键还原。

# 导出完整依赖关系表

如果你想保存一份依赖清单供后续对照分析:

brew list --formula | xargs -I{} sh -c "echo '{}:'; brew deps --installed {}"

更多关于 Homebrew 环境变量和 PATH 配置的内容,可以参考 Zsh 终端配置与 Homebrew 环境修复

# 常见 Homebrew 包功能分类速查表

面对 brew list 中长长的包列表,了解每个包的用途有助于做出正确的清理决策。以下按功能分类整理了常见的 Homebrew 包。

# 开发与构建工具

软件包 主要功能描述
automake 自动生成 Makefile 的工具,常与 Autoconf 配合使用
cmake 跨平台的自动化建构系统,通过 CMakeLists.txt 管理编译过程
make 经典的构建控制工具,通过读取 Makefile 来自动化编译程序
ninja 专注于速度的小型构建系统,常作为 CMake 的后端
git 最流行的分布式版本控制系统
git-sizer 用于计算 Git 存储库的大小及其复杂性的工具
pkgconf 维护库编译/链接标记的工具(pkg-config 的现代替代品)

# 库、解析器与格式支持

软件包 主要功能描述
cryptopp Crypto++ 库,一个功能强大的 C++ 加密算法库
nlohmann-json 现代 C++ 编写的 JSON 解析与操作库(语法接近 Python)
expat 面向流的 XML 解析器库
portaudio 跨平台的音频输入/输出库

# 系统管理与实用工具

软件包 主要功能描述
htop 交互式的系统进程监控器(比普通的 top 更直观)
tmux 终端复用器,允许在一个窗口中开启多个会话并保持后台运行
tree 以树状图形式列出目录内容的命令行工具
smartmontools 用于监控硬盘健康状态(S.M.A.R.T. 数据)的工具
ripgrep 快速的文本搜索工具,支持正则表达式和多线程

# 网络服务器与远程访问

软件包 主要功能描述
nginx 高性能的 HTTP 和反向代理服务器,也支持邮件代理
httpd 即 Apache HTTP Server,经典的开源 Web 服务器
php 广泛使用的通用开源脚本语言,特别适用于 Web 开发
sshpass 用于非交互式 SSH 密码验证的工具(建议谨慎使用)

# 多媒体与压缩

软件包 主要功能描述
ffmpeg 视频处理界的"瑞士军刀",支持几乎所有格式的转码、切片、合并、推流
mpv 基于 MPlayer 的极简且强大的媒体播放器
mplayer 经典的跨平台多媒体播放器
yt-dlp 目前最强的视频下载工具(youtube-dl 的增强分支)
sevenzip 支持 7z、zip、tar.gz 等多种格式的压缩/解压工具

# Bottle 与源码编译的区别

在使用 brew install 安装包时,你可能注意到有些包几秒钟就安装完成,而有些则让电脑风扇狂转、机身发烫。这背后的原因在于 Bottle(预编译二进制包)源码编译 的区别。

# 什么是 Bottle

Bottle 是 Homebrew 官方为主流系统预先编译好的二进制安装包。当你的系统版本和架构匹配时,Homebrew 会直接下载 Bottle 并解压安装,整个过程非常快速。

安装时如果终端显示 Pouring xxx.bottle.tar.gz,说明使用的是 Bottle;如果显示 Building from source,则是在本地编译。

# 为什么有些包必须从源码编译

以下情况会触发源码编译:

  • 使用了测试版(Beta)或非常老旧版本的 macOS,Homebrew 没有准备对应的 Bottle
  • 手动添加了编译选项(如 --build-from-source
  • 某些特殊的依赖项需要特定的编译器环境

# 以 LLVM 为例

LLVM 是世界上最复杂的开源项目之一,包含数百万行 C++ 代码。从源码编译 LLVM 时:

  • 编译任务巨大:需要编译 Clang(C 语言前端)、优化器、各平台后端代码生成器等一系列工具
  • 多核并行满载:Homebrew 默认会调用 CPU 的所有核心进行编译(类似 make -j),所有核心都会处于 100% 负载
  • 内存压力大:编译庞大的 C++ 文件非常消耗内存,如果内存不足触发了磁盘交换(Swap),系统会进一步变慢并发热

建议:安装前先运行 brew update 确保 Homebrew 是最新版本,这样可以最大化获取到 Bottle 的概率,避免不必要的源码编译。

# keg-only 包的概念

# 什么是 keg-only

在 Homebrew 的设计中,某些包被标记为 keg-only,意味着它们虽然安装了,但不会被自动软链接到系统的 PATH 路径下(如 /opt/homebrew/bin)。

# 以 LLVM 为例

当你运行 which gitwhich cmake 时能正常返回路径,但 which llvm 却没有输出。这是因为 LLVM 在 Homebrew 中被标记为 keg-only,原因包括:

  • 避免冲突:macOS 系统自带了基于 LLVM 的 clang 和相关开发工具。如果 Homebrew 强行将其 LLVM 注入 PATH,可能导致编译时系统分不清该用"苹果原装"还是"Homebrew 版"
  • 工具覆盖面广:LLVM 包含非常多工具(如 arnmranlib 等),如果全部链接,会覆盖大量系统底层命令

可以通过 brew info llvm 确认其 keg-only 状态:

brew info llvm
# 输出中会有提示:llvm is keg-only, which means it was not symlinked into /opt/homebrew...

如果确实需要使用 Homebrew 版的 LLVM,可以通过完整路径调用:

# Apple Silicon (M1/M2/M3)
/opt/homebrew/opt/llvm/bin/clang --version

# Intel Mac
/usr/local/opt/llvm/bin/clang --version

或者在 ~/.zshrc 中永久添加路径:

export PATH="/opt/homebrew/opt/llvm/bin:$PATH"
export LDFLAGS="-L/opt/homebrew/opt/llvm/lib"
export CPPFLAGS="-I/opt/homebrew/opt/llvm/include"

# 包清理策略

# 自动清理(最推荐)

在手动删除之前,先让 Homebrew 自己检查哪些包是"孤儿"(即当初作为依赖被安装,但现在已经没有任何软件需要它了):

brew autoremove

这是最安全的清理方式。如果运行后没有反应,说明当前所有包都被某些软件依赖着。

# 清理旧版本缓存

Homebrew 在更新包时会保留旧版本的安装包缓存,运行以下命令可以释放这部分空间:

brew cleanup

建议养成定期运行以下组合的习惯:

brew autoremove  # 自动删除不再被需要的依赖包
brew cleanup     # 清理旧版本的安装包缓存

# 逐包分析思路

对于不确定是否可以删除的包,采用以下分析流程:

第一步:检查是否有其他软件在使用它

brew uses --installed [包名]
  • 如果返回了其他软件名,说明该包正在被依赖,不能删除
  • 如果没有任何返回,说明它是"孤儿"依赖,可以安全删除

第二步:查看包的详细信息

brew info [包名]

第三步:尝试卸载(不加 -f 不会强制执行)

brew uninstall [包名]

如果它还有依赖者,Homebrew 会报错并列出具体是哪些软件在阻止删除。

# 按用途分类的清理建议

brew list 中出现大量底层库时,可以按用途分类来决定是否保留。以下是常见的分类及保留建议:

分类 包含的包 保留条件
图形/3D 建模 assimp, eigen, freetype, libpng 还在使用 Blender、游戏引擎或进行 3D 渲染开发
网络与系统 asio, poco, apr, apr-util, openssl@3 还在进行网络编程或使用 Apache 相关工具
日志与调试 spdlog, log4cxx, fmt, cunit 正在编写 C++ 代码并需要日志输出或单元测试
跨平台/工具链 console_bridge, tinyxml, pcre, pcre2 这些通常是其他大型软件(如 ROS 或数据库工具)的依赖

典型案例分析:如果你的 brew list 中同时出现 console_bridgeeigentinyxmlasiolog4cxx 等包,几乎可以确定曾经安装过 ROS(Robot Operating System)。如果已经不再进行机器人相关开发,这些包通常都可以安全卸载。

对于确认不再需要的包,可以批量卸载:

brew uninstall console_bridge eigen spdlog cunit fmt tinyxml

关于系统层面更深度的清理(如 Ghostscript、XQuartz、LaTeX 等大型软件的卸载),可以参考 macOS 系统深度清理与升级准备

# x86 与 ARM Homebrew 的路径差异与共存处理

在 Apple Silicon(M1/M2/M3)的 Mac 上,可能同时存在两个 Homebrew 实例:

架构 安装路径 说明
ARM(原生) /opt/homebrew 原生运行,性能更好,推荐保留
x86_64(Rosetta 2) /usr/local 通过 Rosetta 2 转译运行,兼容旧版包

# 确认当前使用的架构

which brew
# /opt/homebrew/bin/brew  -> ARM 版
# /usr/local/bin/brew     -> x86 版

# 卸载 x86 版 Homebrew(保留 ARM 版)

如果决定只保留 ARM 版,可以使用官方脚本精准卸载 x86 版本:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/uninstall.sh)" -- --path=/usr/local

注意最后的 --path=/usr/local 参数,它确保只清理 x86 目录而不影响 ARM 版。

# 清理残留文件

卸载脚本执行后,可能需要手动清理残留的 Homebrew 专属目录:

sudo rm -rf /usr/local/Homebrew
sudo rm -rf /usr/local/Caskroom
sudo rm -rf /usr/local/Cellar
sudo rm -rf /usr/local/var/homebrew

注意:不要直接删除整个 /usr/local 目录。该目录是 macOS 系统中标准的"第三方软件存放地",其他软件(如 MySQL、TeX Live)也可能将文件放在此处。应当只清理其中 Homebrew 相关的子目录。

# 清理死链接

卸载后 /usr/local/bin/usr/local/lib 中可能残留大量失效的软链接。macOS 自带的 BSD 版 find 不支持 -xtype 参数,需要使用以下替代命令:

# 先预览将被删除的死链接
find /usr/local/bin -type l ! -exec test -e {} \; -print

# 确认无误后执行删除
find /usr/local/bin -type l ! -exec test -e {} \; -delete
find /usr/local/lib -type l ! -exec test -e {} \; -delete

# 修复环境变量

卸载 x86 版后,检查 ~/.zshrc~/.zprofile,确保 Homebrew 的初始化只有 ARM 版的这一行:

eval "$(/opt/homebrew/bin/brew shellenv)"

同时检查 /etc/paths.d/ 目录,删除不再需要的第三方路径文件:

ls /etc/paths.d/

对于一个 M1/M2/M3 用户,理想的 PATH 结构应该是:

  1. 个人脚本目录(如 ~/mybin
  2. Node/npm 路径(如有使用)
  3. ARM Homebrew(/opt/homebrew/bin
  4. 系统路径(/usr/bin/bin/usr/sbin/sbin

# openssl@3 版本限定符的含义

在 Homebrew 的命名规则中,@ 符号后面的数字被称为版本限定符(Version Specifier),用于解决"新旧版本共存"的问题。

# 具体含义

openssl@3 代表安装的是 OpenSSL 的 3.x 系列版本。由于很多旧软件不兼容 OpenSSL 3.0,必须使用旧的 1.1 版本,Homebrew 采用了带后缀的命名方式来区分:

  • openssl@1.1 -- OpenSSL 1.1.x 系列
  • openssl@3 -- OpenSSL 3.x 系列(当前主流版本)

# 常见的类似写法

包名 说明
python@3.11 指定安装 Python 3.11 版本
node@18 指定安装 Node.js 18 LTS 版本
php@8.1 指定安装 PHP 8.1 版本

# keg-only 特性

带版本限定符的包通常是 keg-only 的,它们虽然安装了,但不会自动软链接到系统路径,以免干扰系统自带的同名工具。

查看 openssl@3 的实际安装路径:

brew --prefix openssl@3
# Apple Silicon: /opt/homebrew/opt/openssl@3
# Intel Mac:    /usr/local/opt/openssl@3

# 依赖角色

openssl@3 在 Homebrew 的依赖体系中扮演着重要角色,许多处理网络通信的库(如 apr-utilpocoasio)都依赖它来实现加密传输(HTTPS/TLS)。因此在清理包时,务必先通过 brew uses --installed openssl@3 确认没有其他软件在使用它。

# 总结

维护一个干净的 Homebrew 环境,建议遵循以下工作流程:

  1. 定期检查:运行 brew leaves 查看顶层包,确认是否有不再需要的软件
  2. 自动清理:执行 brew autoremovebrew cleanup 清除孤儿依赖和旧版本缓存
  3. 逐包分析:对于不确定的包,使用 brew uses --installed [包名] 确认依赖关系后再决定
  4. 环境备份:在大规模清理前,运行 brew bundle dump 备份当前的包列表,以便需要时恢复