ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Mac 上 Homebrew 换国内源:一键脚本解决 brew install 卡顿与超时

Mac 上 Homebrew 换国内源:一键脚本解决 brew install 卡顿与超时 讲个真事上月给朋友的新 Mac 配环境brew install wget敲下去进度条直接卡在Updating Homebrew...环节快十分钟没动。我第一反应不是网不好而是这家伙的 Homebrew 还顶着默认的 GitHub 源在跑。在国内网络环境下Homebrew 从 brew、homebrew-core 到 bottle 二进制包全程走 GitHub 那几条链路卡、慢、超时几乎成了常态。这篇里的脚本就是我当时顺手整理的解决一件事在苹果电脑上一条命令把 Homebrew 默认源切成国内源没装过的直接带源装想还原、想卸载也有一键脚本兜底。适合刚入手 Mac 的开发者、被 brew install 折磨过的人也适合负责团队新机装机脚本的同学抄作业。1. 换源解决的不是慢而是 Homebrew 的下载链路问题很多人以为换源就是改个网址的事真正理解之后你会发现Homebrew 的下载链路比想象中要复杂这也是为什么网上经常出现我明明换了源怎么还是慢的疑问。1.1 一次 brew install 背后到底访问了哪些地址以我用 Intel Mac 和老版本 Homebrew 的体验来说一条brew install触发的东西有先检查 brew 本体所在的 Git 仓库是否落后git pull然后去拉取 formula 索引再根据平台架构去下载对应的 bottle 预编译包。这三个动作分别对应不同的下载地址brew 本体仓库和 homebrew-core、homebrew-cask 配方仓库默认都在github.com/Homebrew/下公式索引 API新版 Homebrew 默认从formulae.brew.sh/api/formula.jws.json拉取bottle 预编译包默认从ghcr.ioGitHub Container Registry下载这四条链路任何一条慢体现出来的结果都一样brew install卡在某个进度条或者干脆报curl超时。换源的本质就是把这几条链路的默认地址全部替换成国内可访问的镜像仓库而不是只换其中某一个。这也是我写脚本时坚持把HOMEBREW_API_DOMAIN、HOMEBREW_BOTTLE_DOMAIN、git remote 一起处理的原因。1.2 为什么网上很多换源教程没效果我见过不少人照着帖子改了HOMEBREW_BOTTLE_DOMAIN结果brew update依然卡死。查下来通常是漏了两处一是HOMEBREW_API_DOMAIN没设公式索引还在走官方接口二是 brew 本体仓库的 git remote 没切brew update那一下还是要跟 GitHub 打交道。补充一个细节如果你用的是较老的 Homebrew 版本它安装 formula 时还依赖 homebrew-core 这个本地 tap 仓库这意味着换源必须包含 homebrew-core 的 git remote 切换。而 Homebrew 4.x 以后默认走 JSON API反而没那么依赖本地 tap。所以脚本里我做了兼容处理本地存在 homebrew-core 目录就切不存在就直接走 API两边都不会出问题。2. 镜像源选型清华、中科大、阿里我用一份对比表说话国内能换的 Homebrew 镜像源不少但每个源同步策略、覆盖范围、抗压能力都不一样。选错了不是不能用是高峰期可能比官方源还慢。这里我把自己实际测过的几个源拉出来对比。镜像站brew/公式仓库bottle 下载API 接口个人评级清华 TUNA支持支持支持首选教育网线路快同步及时中科大 USTC支持支持支持老牌稳定适合中科大附近网络阿里云支持支持支持家宽线路好高峰期偶尔限速腾讯云支持支持支持覆盖面不错但文档相对少2.1 清华源为什么被推荐得最多清华 TUNA 镜像站在国内运维时间长homebrew-bottles这个目录下不光有 bottle 包还有对应的 API JSON 数据两者是同一条 URL 前缀下的不同路径。这样的好处是配置简单我只要把HOMEBREW_BOTTLE_DOMAIN和HOMEBREW_API_DOMAIN都指到同一个根域名Homebrew 就能自动拼出正确的下载路径。实际体验上清华源的同步延迟通常在可接受范围内不会出现官方发了新版本、镜像却要等好几天的情况。对大部分用户来说换源后速度提升最明显的场景就是brew install下载 bottle 包几百 MB 的东西官方源可能跑到一半丢连接清华源基本满速跑完。2.2 中科大源的特殊技巧中科大源有个特点它把brew.git、homebrew-core.git、homebrew-cask.git分别放在平级路径下而且对老版本 Homebrew 的兼容做得不错。如果你还在用某些特殊渠道装的旧版本 brewUSTC 会是更省心的选择。据说中科大建议对HOME_DIR下的 Homebrew 目录做remote set-url切换这个动作恰恰是我脚本里核心的操作。各家源地址差异主要就是 URL 前缀所以我给脚本预留了三个源参数实际使用中想换哪个改一个参数就行。3. install.sh 完整实现从零安装与已装切换两条路径这个脚本我写了两个功能分支。第一分支是检测到 Mac 上压根没装 Homebrew直接利用官方安装脚本支持的环境变量实现带着国内源完成安装第二分支是已装过 Homebrew用git remote set-url把仓库地址切到镜像并写入持久化环境变量。3.1 脚本的整体骨架脚本主要有四个动作选源、检测、切换、写入配置。我特意把选源放在最前面方便你改成自己常用的镜像#!/bin/bash set -euo pipefail # # install-mirror.sh # 用法: bash install-mirror.sh [tuna|ustc|aliyun] # 功能: Homebrew 一键换国内源未安装时自动带源安装 # MIRROR_FLAG${1:-tuna} case $MIRROR_FLAG in tuna) BREW_GIThttps://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git CORE_GIThttps://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git CASK_GIThttps://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-cask.git API_DOMAINhttps://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles/api BOTTLE_DOMAINhttps://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles ;; ustc) BREW_GIThttps://mirrors.ustc.edu.cn/brew.git CORE_GIThttps://mirrors.ustc.edu.cn/homebrew-core.git CASK_GIThttps://mirrors.ustc.edu.cn/homebrew-cask.git API_DOMAINhttps://mirrors.ustc.edu.cn/homebrew-bottles/api BOTTLE_DOMAINhttps://mirrors.ustc.edu.cn/homebrew-bottles ;; aliyun) BREW_GIThttps://mirrors.aliyun.com/homebrew/brew.git CORE_GIThttps://mirrors.aliyun.com/homebrew/homebrew-core.git CASK_GIThttps://mirrors.aliyun.com/homebrew/homebrew-cask.git API_DOMAINhttps://mirrors.aliyun.com/homebrew/homebrew-bottles/api BOTTLE_DOMAINhttps://mirrors.aliyun.com/homebrew/homebrew-bottles ;; *) echo 未知镜像源: $MIRROR_FLAG可用值: tuna / ustc / aliyun exit 1 ;; esac提示不确定选哪个源的时候默认tuna就好。教育网、家宽、移动、联通我都试过综合表现最稳。3.2 分支一未装 Homebrew直接用国内源完成安装官方安装脚本本身就支持通过环境变量指定 git 仓库地址。很多人不知道这一点还在傻等官方安装脚本慢慢拉 GitHub这个做法太不推荐了。完整的安装分支如下if ! command -v brew /dev/null 21; then echo 未检测到 Homebrew使用带国内源的方式安装... export HOMEBREW_BREW_GIT_REMOTE$BREW_GIT export HOMEBREW_CORE_GIT_REMOTE$CORE_GIT export HOMEBREW_CASK_GIT_REMOTE$CASK_GIT export HOMEBREW_API_DOMAIN$API_DOMAIN export HOMEBREW_BOTTLE_DOMAIN$BOTTLE_DOMAIN /bin/bash -c $(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh) else echo 已检测到 Homebrew执行仓库源切换... brew_prefix$(brew --prefix) git -C $brew_prefix remote set-url origin $BREW_GIT core_tap$brew_prefix/Library/Taps/homebrew/homebrew-core cask_tap$brew_prefix/Library/Taps/homebrew/homebrew-cask if [[ -d $core_tap ]]; then git -C $core_tap remote set-url origin $CORE_GIT fi if [[ -d $cask_tap ]]; then git -C $cask_tap remote set-url origin $CASK_GIT fi fi这里需要注意brew --prefix在 Apple Silicon 机器上通常是/opt/homebrewIntel 机型是/usr/local用命令取出来的值比写死路径可靠得多。另外在新版 Homebrew 里homebrew-core 可能不会以本地 tap 的形式存在所以我都加了-d目录判断目录不存在就跳过不影响整体。3.3 分支二把环境变量写成持久化配置光切 git remote 还不够bottle 下载和 API 接口索引还要靠环境变量。所以脚本最后的动作是写入 shell 配置文件。这里注意几个细节macOS 默认 shell 是 zsh但不少人装了 bash所以脚本要自动检测写入前先把旧的export HOMEBREW_开头行清掉避免换源后残留旧配置导致脚本反复追加不要用echo $var file这种粗暴追加法要先清理再写入detect_profile() { if [[ -f $HOME/.zprofile ]]; then echo $HOME/.zprofile elif [[ -f $HOME/.zshrc ]]; then echo $HOME/.zshrc elif [[ -f $HOME/.bash_profile ]]; then echo $HOME/.bash_profile else echo $HOME/.zshrc fi } PROFILE$(detect_profile) sed -i /^export HOMEBREW_/d $PROFILE 2/dev/null || true cat $PROFILE EOF export HOMEBREW_API_DOMAIN$API_DOMAIN export HOMEBREW_BOTTLE_DOMAIN$BOTTLE_DOMAIN export HOMEBREW_BREW_GIT_REMOTE$BREW_GIT export HOMEBREW_CORE_GIT_REMOTE$CORE_GIT export HOMEBREW_CASK_GIT_REMOTE$CASK_GIT EOF echo 环境变量已写入 $PROFILE echo 执行 source $PROFILE 后建议先运行一次 brew update这里还隐藏了一个细节为什么连HOMEBREW_CORE_GIT_REMOTE这种新版本已经不太用的变量也要写因为老版本和不走 API 的配置模式下缺了它brew update依然会跑回官方地址。写全了兼容性最好代价无非是多几行 export。4. uninstall.sh 与残留清理卸载不完全比不卸载更难受Homebrew 卸载比安装更容易翻车。官方提供了 uninstall 脚本但它同样需要从 GitHub 拉取而且即使卸载完成shell 环境变量、缓存目录、日志目录都会残留。所以我做卸载脚本时把它分成三步还原源、卸载本体、清理残留。4.1 先用官方卸载脚本网络兜底方案再手动删除官方脚本能自动识别 brew 装在哪、要不要保留某些目录比手工rm -rf安全得多。所以在卸载脚本里我优先调用官方脚本只有命令失败才提示手动删除echo 第一步执行官方卸载脚本 if ! /bin/bash -c $(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/uninstall.sh) -- --skip-cache-and-logs; then echo 官方卸载脚本执行失败尝试手动删除 brew_prefix$(brew --prefix) if [[ -d $brew_prefix ]]; then rm -rf $brew_prefix echo 已删除 $brew_prefix fi fi--skip-cache-and-logs参数的意思是卸载时不用动缓存和日志目录这两个目录后面由我自己接管。如果你装了 MySQL、PostgreSQL 这类把数据放在$(brew --prefix)/var下的软件官方卸载脚本会提示你数据残留记得提前备份。4.2 清理环境变量与缓存目录卸载完成后~/.zprofile或~/.zshrc里的 export 行不会自己消失。下次新装 Homebrew 时这些残留变量会继续把新 brew 指向旧镜像不算致命但会带来为什么我重装还是这么慢的困惑。所以清理脚本要把它们一并处理echo 第二步清理 shell 环境变量残留 PROFILE$(detect_profile) sed -i /^export HOMEBREW_/d $PROFILE 2/dev/null || true echo 已从 $PROFILE 中移除 HOMEBREW_ 相关环境变量 echo 第三步清理缓存与日志目录 rm -rf $HOME/Library/Caches/Homebrew rm -rf $HOME/Library/Logs/Homebrew echo 缓存与日志已清理写完这个脚本我特意测试过先手动往 zshrc 里塞了几行export HOMEBREW_BOTTLE_DOMAIN...跑完脚本后grep -n HOMEBREW ~/.zshrc输出为空清理得很干净。另外临时文件目录、旧版安装器留下的/Library/Developer/CommandLineTools是否要删看个人需求一般不建议动系统工具链。5. 换源后的验证与常见坑环境变量、权限和缓存这三道坎脚本写完了不代表万事大吉。我在测试时踩过几个坑每一步都有对应的排查方法这里按先验证、后处理坑的顺序说。5.1 换源是否生效看这三处输出第一处看brew config。终端里执行brew config正常情况下能看到类似下面的输出HOMEBREW_BOTTLE_DOMAIN: https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottlesHOMEBREW_API_DOMAIN: https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles/api如果这里还是空或者写着ghcr.io那就是环境变量没 load 进来执行source ~/.zprofile再重开一个终端会话试试。第二处看 brew 本体的 git remote。执行git -C $(brew --prefix) remote -v输出里的origin应该指向mirrors.tuna.tsinghua.edu.cn开头的地址如果还是github.com/Homebrew/brew.git说明切换分支没有真正生效。第三处重新装一个包观察下载 URL。挑一个包比如brew install htop在下载 bottle 时注意终端输出的地址。如果是指向清华或者中科大源的homebrew-bottles说明 bottle 路径没问题。5.2 权限问题Cannot write to /opt/homebrew的根源Homebrew 对安装目录的属主非常敏感。有些时候我们为了装软件会对/opt/homebrew或者/usr/local执行sudo chown -R但某些文件属主还是 root导致brew update报出权限类的错误。这种问题用脚本没法根治正确的检查姿势是ls -ld $(brew --prefix)输出应该显示当前用户有读写权限。如果没有执行sudo chown -R $(whoami):admin $(brew --prefix)如果权限修复后brew update依然报错可以用brew doctor看看还有什么历史遗留问题。说实话很多 Mac 上 Homebrew 的怪毛病brew doctor都能直接给出修复建议这是最被低估的排查命令。5.3 缓存导致的假换源换源最隐蔽的坑是缓存。你换源之前Homebrew 已经把某些软件的下载地址缓存到了~/Library/Caches/Homebrew。换源后再装同一个包时如果缓存里有对应版本的下载记录它可能还在用旧的下载地址做校验甚至直接复用断点缓存的数据。遇到这种情况我的经验是换源后先做一次彻底清理rm -rf $HOME/Library/Caches/Homebrew/downloads然后重新安装目标软件。别嫌这一步多余它能消灭九成换源没效果的误判。6. 周边工具的联动pip、npm、Jenkins 换源思路与 Homebrew 一致Homebrew 这套换源逻辑其实能复用到很多其他包管理器。近期常看到有人搜 pip 国内源、npm 国内源、Jenkins 插件国内源底层套路惊人地相似定位默认下载地址改成镜像站对应路径然后写入配置文件持久化。6.1 pip 和 npm 的换源操作pip 如果觉得安装第三方库慢最简单的是执行pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple这条命令和 Homebrew 写入HOMEBREW_API_DOMAIN的思路一模一样都是把从哪里下载包这个配置写进用户级配置文件。npm 则是npm config set registry https://registry.npmmirror.com反观 Jenkins 插件中心本质上就是把update-center.json的下载地址替换成 Jenkins 官方中文镜像提供的 URL配置完重启 Jenkins 后插件安装速度会有肉眼可见的提升。6.2 换源这几个通用坑跨工具都成立我踩过这么多换源方案后发现不管换什么源坑基本都是那几类一觉醒来镜像站同步滞后导致装到了旧版本缓存没有清理导致下载的还是旧地址配置文件格式写错导致工具直接罢工。所以无论换哪个源建议遵循同一个流程先备份原配置、再修改、然后验证。Homebrew 这套脚本只是把这个流程自动化了核心方法论是可以迁移到所有包管理器的。顺便说一句脚本里的源地址如果哪天发现失效先去对应镜像站的首页确认路径是否变动Homebrew 镜像的 URL 组织方式偶尔会调整改一下case分支里的地址就能继续用。
返回列表