外观
用 Brewfile 声明这台机器装了什么
用了一年之后,brew list 会列出几十上百个包,其中一部分你已经忘了为什么装。Brewfile 把这份清单变成一个可以读、可以提交、可以在新机器上执行的文件。
brew bundle 是 Homebrew 的内置子命令,不需要额外 tap。Homebrew 官方文档:Brew Bundle 与 Brewfile
开始之前
需要装好 Homebrew,见使用 Homebrew 管理工具。本文命令不会卸载任何东西,除非你明确执行 cleanup——那一节单独标注了风险。
导出当前状态
sh
brew bundle dump --describedump 把当前已安装的内容记录成 Brewfile,写在当前目录。--describe 给每一项加上一行说明注释——这是这篇里最值得记住的参数,因为半年后你需要的正是这些说明。
当前目录已有 Brewfile 时命令会失败,这是保护机制。确认可以覆盖再加 --force。
打开生成的文件,会看到几类条目:
ruby
tap "homebrew/cask-fonts"
brew "ripgrep"
cask "iina"
mas "Klib", id: 1439578330
vscode "editorconfig.editorconfig"tap是额外的软件源。brew是命令行包(formula)。cask是图形界面应用。mas是 App Store 应用,需要另外装mas这个命令行工具,并且已在 App Store 登录。vscode是编辑器扩展。
Brewfile 按 Ruby 语法解析,所以可以写条件判断,但这会让文件难读。先保持它是一份平铺的清单。
核对与重建
检查当前机器与清单是否一致:
sh
brew bundle check --verbosecheck 只报告差异,不做任何改动。--verbose 让它列出具体缺哪些,而不是只给一个结论。
按清单安装:
sh
brew bundle install已经装好的项目会被跳过,所以重复执行是安全的。这条命令也可以简写成 brew bundle。
cleanup 会卸载东西
sh
brew bundle cleanup不加参数时它只列出清单里没有、但机器上装了的包,不执行删除。确认列表无误后,才用 --force 真正卸载。
务必先看列表。刚从别的机器拿来一份 Brewfile 就直接 cleanup --force,会卸载掉这台机器上你需要但清单里没有的软件。卸载图形应用时,应用的配置和数据未必一并清除,相关说明见卸载应用。
它不锁定版本
这是最容易误解的地方。Homebrew 是滚动更新的包管理器,brew bundle 没有也不会有 lock 文件的概念。同一份 Brewfile 今天和三个月后执行,装到的版本不同。
所以 Brewfile 回答的是「装了哪些软件」,不是「装了哪些版本」。需要锁定运行时版本,那是另一件事,用 mise 管理并把版本写进项目里的配置文件。
默认情况下 brew bundle install 会顺带升级已装的包。不希望它这样做时加 --no-upgrade。
检查结果
在一个临时目录里 brew bundle dump --describe,打开文件确认:条目数量和你的实际安装大致相符,每行都有说明注释,没有出现你不认识的 tap。
然后执行 brew bundle check --verbose,应当报告没有缺失。如果报告缺少 mas 条目,说明你还没装 mas 工具或没在 App Store 登录——这属于预期行为,不是清单出错。
动手练习
用一个可丢弃的目录完成一轮,不影响现有安装:
mkdir -p /tmp/brewfile-test && cd /tmp/brewfile-testbrew bundle dump --describe,通读生成的文件,标出三个你已经想不起用途的包。brew bundle check --verbose,确认结果是一致。- 手工删掉文件里的一行,再执行一次
check,观察它如何报告差异。 - 不要在这个目录执行
cleanup --force。
成功标准是能解释第 4 步的输出,并且对第 2 步标出的三个包做出决定:查清用途还是卸载。清单的价值不在于文件本身,而在于它逼你面对那些忘了为什么装的东西。