ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Apple Silicon Mac 上 Homebrew 环境变量配置与自定义路径实战

Apple Silicon Mac 上 Homebrew 环境变量配置与自定义路径实战 拿到一台 Apple Silicon 的 Mac重装开发环境第一步几乎都是 Homebrew。但这个第一步现在坑特别多网上教程有的让你改~/.bash_profile有的让你把 brew 装到/opt/homebrew还有人说环境变量要写进~/.zprofile——等你真的敲brew install xxx时可能又撞上command not found或者安装脚本跑到一半就中断。这篇文章专门解决一个具体问题macOSApple Silicon上 Homebrew 环境变量到底怎么配以及如果想把 Homebrew 放到~/Develop/Tools/Homebrew这种自定义目录应该怎么做。后面会把实际踩过的报错和修复过程完整列出来覆盖从终端找不到 brew 到镜像源、Rosetta 前缀冲突、系统版本不支持这些高频问题。1. Apple Silicon 上 Homebrew 的地盘全变了/opt/homebrew、zsh 与 .zprofile1.1 Intel 时代和 Apple Silicon 时代的路径差异先搞清楚一件事同一句export PATH/opt/homebrew/bin:$PATH在 Intel Mac 上反而不对。Intel 时代 Homebrew 的默认前缀是/usr/localApple Silicon 上强制使用/opt/homebrew。为什么根本原因是两种架构的二进制不能混用。Apple Silicon 的 Homebrew 会下载、编译 arm64 架构的软件包如果把这类软件塞进/usr/local和 Intel 时代遗留的 x86_64 工具混在一个目录which python3、which node这类命令的结果会变得不可控。所以 Homebrew 官方的做法是在 Apple Silicon 上直接把地盘划到/opt/homebrew彼此隔离。这也是为什么很多老教程会坑人。你拿一篇 2019 年之前的教程照做它让你往 PATH 里加/usr/local/bin在 Apple Silicon 上当然看起来什么都没发生——因为你的 brew 根本不在那里。安装时如果看到Cannot install in Homebrew on ARM processor in Intel default prefix (/usr/local)!也是这个逻辑在保护你。1.2 zsh 接管默认 shell 后配置文件也跟着变了第二个容易混淆的点是配置文件。macOS Catalina 之后默认 shell 从 bash 换成了 zshApple Silicon 机器出厂就是 zsh。所以那些教你改~/.bash_profile的教程在 Apple Silicon 上基本可以直接忽略了——zsh 默认不会读 bash 的配置文件。zsh 的配置读取顺序大致是配置文件读取时机适合放什么~/.zshenv所有 zsh 进程启动时尽量少用容易污染非交互环境~/.zprofile登录 shell 启动时比如打开新的终端窗口PATH、全局环境变量~/.zshrc每个交互式 zsh 启动时别名、提示符、插件、函数~/.zlogin登录 shell 启动的后期需要等前面配置就绪的命令~/.zlogout登录 shell 退出时收尾清理Homebrew 官方安装脚本提示你写入的是~/.zprofile因为 macOS 的 Terminal、iTerm2 开新窗口默认走登录 shell.zprofile会在.zshrc之前加载适合放PATH这类全局环境变量。1.3 环境变量到底写进哪个文件.zprofile 与 .zshrc 的取舍这里我想多说两句实战取舍因为网上观点经常打架。如果你只在 Terminal.app 或 iTerm2 里用命令行写~/.zprofile就够了官方安装脚本也是这么建议的。但如果你经常用 VSCode、IntelliJ 这类 IDE 的内置终端或者喜欢在 tmux 里开新窗格情况就不一样了IDE 内置终端不一定以登录 shell 方式启动tmux 新窗格很多时候也不走登录 shell它们只读~/.zshrc。这时候你只配了.zprofilebrew 命令就会时灵时不灵。我的做法是环境变量直接写在~/.zshrc里。.zprofile留给其他必须登录时只执行一次的逻辑。如果你担心 IDE 内置终端不读.zshrc其实绝大多数 IDE 的终端都按交互 shell 启动是读.zshrc的。当然两个文件都写同一行export PATH也不会出错只是 PATH 里会有重复项查找时不影响结果。二选一的话我更推荐.zshrc覆盖场景更广。2. 为什么有人要把 Homebrew 装进 ~/Develop/Tools/Homebrew以及这样做的代价2.1 自定义目录的真实动机标题里那个路径~/Develop/Tools/Homebrew一看就是想把开发工具收敛到统一目录。我见过不少开发者的目录规划是这样的~/Develop ├── Projects ├── Archive └── Tools ├── Homebrew ├── Nodejs └── ...好处很明显备份时整个~/Develop一起打包换机器时知道自己的工具链都装在哪公司安全策略要求开发工具不能散落到系统目录只能放用户目录还有一些人有根目录洁癖不想动不动就往/opt下写东西。这些动机我都理解。但必须把丑话说在前面自定义路径不是 Homebrew 官方主推的玩法它主要服务于标准安装/opt/homebrew。你选择自定义路径之后网上大部分默认安装的教程都会部分失效遇到问题排查时也不能直接抄答案。2.2 自定义路径后Homebrew 是怎么定位前缀的Homebrew 的前缀prefix不是靠某个环境变量硬编码的而是由bin/brew这个可执行文件的位置倒推出来的。brew脚本运行时会找到自己所在的bin目录父目录就是 prefix。也就是说只要brew这个脚本在~/Develop/Tools/Homebrew/bin/brewHomebrew 就会认为~/Develop/Tools/Homebrew就是它的前缀后续的 Cellar、Cask、Taps 都会以它为根。这一点其实是自定义安装可行性的基石。你不需要去设置HOMEBREW_PREFIX来指定前缀设置了反而可能造成混乱。真正需要的只是让 shell 能找到brew也就是 PATH 里包含~/Develop/Tools/Homebrew/bin。2.3 代价与我的建议说点实在的代价少数 formula 在编译或运行时假设了标准路径会有关联问题虽然不频繁但遇到一次就够你折腾半天。网上关于/opt/homebrew的报错解决方案多自定义路径的少排查问题时往往要把别人的答案翻译一遍。官方安装脚本不支持你指定这样的自定义前缀必须走 git clone 方式安装步骤要多几步。所以我通常建议如果是个人电脑老老实实装到/opt/homebrew如果是公司统一开发机、或你确实有统一目录管理的强需求才考虑自定义路径。安装方式都是可逆的装到自定义目录以后想切回标准路径也不难把所有环境变量配置清掉、重新跑官方脚本就行。3. 自定义路径安装实操git clone、目录结构与环境变量一步到位3.1 用 git clone 方式安装 brew 本体先确保已经安装了 Xcode Command Line Tools。没有的话装一半会报xcrun: error。打开终端执行xcode-select --install弹窗提示时点安装等它跑完。装完可以验证一下xcode-select -p能看到/Library/Developer/CommandLineTools就算就绪。然后开始安装 brew 本体mkdir -p ~/Develop/Tools cd ~/Develop/Tools git clone https://github.com/Homebrew/brew Homebrew这会在~/Develop/Tools下面创建一个Homebrew目录里面就是 brew 命令本体。注意这一步只是把管理工具拉下来了不包含软件包仓库。如果你直接用brew install可能会发现找不到 formula。3.2 补齐 homebrew-core 目录Homebrew 4.0 之后默认从 JSON API 获取 formula 信息理论上不拉homebrew-core也能跑。但自定义安装时我建议一次补齐避免后续出现tap 缺失之类的玄学报错。mkdir -p ~/Develop/Tools/Homebrew/Taps/homebrew git clone https://github.com/Homebrew/homebrew-core ~/Develop/Tools/Homebrew/Taps/homebrew/homebrew-core如果你所在的网络环境拉 GitHub 很慢clone 的仓库地址也可以换成国内镜像我后面第 6 章会专门说镜像配置。先按官方地址来。cd ~/Develop/Tools/Homebrew brew update第一次brew update会拉取元数据可能比较慢跑完以后 brew 本体就具备完整的安装能力了。3.3 写入 .zprofile 并用 brew shellenv 接管环境变量这里不要手写export PATH$HOME/Develop/Tools/Homebrew/bin:$PATH官方更推荐用brew shellenv动态生成环境变量。它会一次性输出HOMEBREW_PREFIX、HOMEBREW_CELLAR、HOMEBREW_REPOSITORY、PATH、MANPATH、INFOPATH这些内容比自己手工 export 干净得多。执行echo eval $($HOME/Develop/Tools/Homebrew/bin/brew shellenv) ~/.zprofile source ~/.zprofile如果你前面的选择是写在.zshrc就把目标文件换成.zshrc内容一样。注意这行里$HOME不要想当然展开成绝对路径单引号包裹会让它保持变量形式zsh 每次启动时才展开这样以后如果系统迁移、用户名变化配置还能继续用。为什么不推荐手动export PATH因为brew shellenv除了 PATH 还会维护MANPATH和INFOPATH手动写的话很容易漏掉 man 手册路径导致man brew都直接报错。不过现实中手动写也不是不能用只是后续维护起来细节多。既然官方给了更稳的方式建议直接用。3.4 验证是否安装成功安装完不要急着装软件先做三件事which brew brew --version brew --prefix期望输出which brew指向/Users/你的用户名/Develop/Tools/Homebrew/bin/brewbrew --version正常显示版本号brew --prefix输出/Users/你的用户名/Develop/Tools/Homebrew如果which brew没输出说明 PATH 没生效检查.zprofile或.zshrc是否真的写入成功然后重新source。如果前缀和预期不一致多半是目录结构不对检查brew是否真的在~/Develop/Tools/Homebrew/bin/下。注意brew --prefix是 Homebrew 内部根据可执行文件位置推导出来的不是从环境变量读的。它输出的内容和你预想的一致性是自定义安装是否成功的最重要指标。4. 标准路径 /opt/homebrew 下的环境变量配置姿势brew shellenv 与 .zprofile/.zshrc 的取舍4.1 官方安装脚本与自动提示的两条命令回到默认方案。Apple Silicon 上跑官方安装脚本/bin/bash -c $(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)安装完成以后终端会明确提示你执行两条命令(echo; echo eval $(/opt/homebrew/bin/brew shellenv)) /Users/你的用户名/.zprofile eval $(/opt/homebrew/bin/brew shellenv)第二条命令只是在当前窗口立即生效第一条是永久写入。很多人第一次装完 brew直接关掉窗口再打开发现brew不存在就是因为只执行了第二条、没执行第一条或者把第一条里的路径写错了。4.2 为什么官方推荐 eval $(brew shellenv) 而不是手动 export PATHbrew shellenv输出大概长这样export HOMEBREW_PREFIX/opt/homebrew; export HOMEBREW_CELLAR/opt/homebrew/Cellar; export HOMEBREW_REPOSITORY/opt/homebrew; export PATH/opt/homebrew/bin:/opt/homebrew/sbin${PATH:$PATH}; export MANPATH/opt/homebrew/share/man${MANPATH:$MANPATH}; export INFOPATH/opt/homebrew/share/info:${INFOPATH:-};看到区别了吗它不只会设置 PATH还设置了HOMEBREW_PREFIX、HOMEBREW_CELLAR、HOMEBREW_REPOSITORY以及MANPATH和INFOPATH。手动export PATH只能管住命令查找管不了 man 手册和 info 文档。中途升级 Homebrew 时brew shellenv会跟着新版本重新计算手动写死路径则容易留下旧路径残留。4.3 实际项目中 .zprofile 和 .zshrc 怎么选正常情况下官方提示写入.zprofile这在 Terminal 开新窗口的场景下好使。但如果你也同时用 IDE 内置终端我前面说过了直接加到.zshrc更省心。不放心的话也可以保持官方推荐不动额外在.zshrc里补一行if [ -f /opt/homebrew/bin/brew ]; then eval $(/opt/homebrew/bin/brew shellenv) fi这样两边的场景都覆盖了。注意加了判断即使/opt/homebrew目录不存在zsh 启动也不会报错。这算是我在实际项目中比较喜欢的一种写法。5. command not found: brew——先分清是没装上还是没进 PATH5.1 最经典的装完却敲不出 brew排查链路zsh: command not found: brew是出现频率最高的报错。很多人第一反应是重装其实绝大多数情况是 PATH 没配好。排查顺序很重要不要一上来就重新执行安装脚本。第一步先确认 brew 本体到底存不存在ls -l /opt/homebrew/bin/brew # 标准路径 ls -l ~/Develop/Tools/Homebrew/bin/brew # 自定义路径文件存在说明装上了问题出在 shell 找不到它文件不存在才需要考虑重新安装。第二步看 PATH 里有没有对应的 bin 目录echo $PATH | tr : \n | grep -n homebrew没输出说明配置没写进去或者写了但没加载。第三步检查当前 shell 到底是哪个、配置文件是否选对了echo $SHELL ls -la ~/.zprofile ~/.zshrc ~/.bash_profile 2/dev/null如果echo $SHELL输出/bin/zsh那你改.bash_profile就是白改。如果文件存在但没生效大概率是当前窗口没有重新加载手动执行source ~/.zshrc这一套链路走下来九成的command not found都能解决。5.2 PATH 的写入顺序、引号展开和 shell 切换细节还有几个细节很多人会忽略。第一个是追加顺序。export PATH/opt/homebrew/bin:$PATH把 Homebrew 放在最前面意味着which python3时优先找到 brew 装的版本如果写成export PATH$PATH:/opt/homebrew/bin系统自带的/usr/bin/python3会优先命中经常出现明明 brew install 了 python但还是老版本的诡异现象。所以要注意把 Homebrew 的 bin 放在前面。第二个是引号问题。配置里$HOME有没有被正确保留决定了路径展开是否正确。如果手写配置时把路径写成了~/Develop/Tools/Homebrew /bin中间不小心多了一个空格或者用了特殊字符没加引号PATH 里就会混入错误项。建议粘贴配置时留意路径中含空格时一定加双引号。第三个是 shell 切换。有些用户从 bash 切到 zsh或者从 zsh 切回 bash但只在一个 shell 的配置文件里写了路径另一个 shell 里照旧找不到 brew。可以检查一下echo $SHELL然后确认对应的配置文件。Apple Silicon 上基本建议统一用 zsh少给自己找麻烦。5.3 非交互场景脚本、定时任务里 brew 消失的问题还有一种隐蔽情况终端里brew正常但在脚本或定时任务里跑却报command not found。原因在于 cron、launchd 启动的非交互 shell 不会加载.zshrc和.zprofile。这种场景需要在脚本开头显式指定路径或者先加载配置#!/bin/zsh source ~/.zprofile brew update如果你写的是 launchd plist建议不要在 plist 的ProgramArguments里直接写brew而是写绝对路径。可以用which brew查出来比如/opt/homebrew/bin/brew。这个坑我自己在写自动更新脚本时踩过排查了半天才反应过来是非交互环境根本没加载配置。6. 安装链路报错实录镜像源、Rosetta 前缀、老系统与卸载残留6.1 下载安装脚本超时换用国内镜像源的标准操作官方安装脚本是从raw.githubusercontent.com拉取的这个地址在某些网络环境下经常连接超时报错形如curl: (7) Failed to connect to raw.githubusercontent.com port 443: Operation timed out这种时候别死磕官方地址换国内镜像源是通用做法。中科大镜像提供的安装脚本地址是export HOMEBREW_INSTALL_FROM_API1 export HOMEBREW_BREW_GIT_REMOTEhttps://mirrors.ustc.edu.cn/brew.git export HOMEBREW_CORE_GIT_REMOTEhttps://mirrors.ustc.edu.cn/homebrew-core.git export HOMEBREW_API_DOMAINhttps://mirrors.ustc.edu.cn/homebrew-bottles/api export HOMEBREW_BOTTLE_DOMAINhttps://mirrors.ustc.edu.cn/homebrew-bottles /bin/bash -c $(curl -fsSL https://mirrors.ustc.edu.cn/misc/brew-install.sh)执行完以后brew 本体和 core tap 都会走镜像地址。如果是已经装完 Homebrew、只是后续brew update或brew install很慢同样可以设置这几个环境变量再加入.zprofile或.zshrc让每次 shell 启动自动走镜像。清华的 TUNA 镜像也提供类似变量习惯用哪个都行。要提醒一点换镜像时尽量保持HOMEBREW_BREW_GIT_REMOTE、HOMEBREW_CORE_GIT_REMOTE、HOMEBREW_BOTTLE_DOMAIN属于同一家源不要一股脑混着填否则可能出现元数据来自 A 源、二进制来自 B 源的错位排查起来更麻烦。6.2 Cannot install in Homebrew on ARM processor in Intel default prefix 的 Rosetta 坑这个报错很典型Error: Cannot install in Homebrew on ARM processor in Intel default prefix (/usr/local)!Apple Silicon 的 CPU 明明是 ARM安装脚本却想装到 Intel 默认前缀/usr/local两者冲突直接中止。出现这种情况绝大多数是因为终端进程是在 Rosetta 模拟下运行的。最常见的原因是用户在终端 App 的设置里勾选了使用 Rosetta 打开或者手动用arch -x86_64 zsh启动了终端。排查和解决uname -m期望输出arm64。如果输出是x86_64说明当前环境是 Rosetta 模拟的。处理办法退出终端右键 Terminal.app 或 iTerm2 的图标确认没有勾选使用 Rosetta 打开或者退出后重新打开。一般这样就能恢复正常。顺带一提有些不了解情况的用户会反过来想既然想装 x86_64 的 Homebrew那就干脆让它装到/usr/local好了。但 Apple Silicon 上原生方案就是 arm64 Homebrew别自己制造双架构混乱。日常使用中绝大多数软件都能跑在 arm64 下没必要用 Rosetta 跑整套工具链。6.3 老系统版本不再支持、卸载残留与权限类报错Homebrew 已经不维护旧版 macOS 了。如果你的系统是 macOS 10.15 或更早安装或更新时可能看到类似Homebrew no longer supports macOS 10.15的提示之后安装流程直接中止。这种情况官方不会修复绕过的办法也不建议最实际的路就是升级系统或者用相对历史版本的安装方式。自制项目可以固定旧版本 tap但日常使用不建议在一台老系统上强行维护 Homebrew。再一个是卸载不彻底的残留问题。有些人以前装过 Homebrew后来卸载了但重新安装时报It seems Homebrew is already installed。这时候需要确认残留到底在哪ls -ld /opt/homebrew 2/dev/null cat ~/.zprofile 2/dev/null | grep -n brew cat ~/.zshrc 2/dev/null | grep -n brew确认残留目录是 Homebrew 自己的再删除sudo rm -rf /opt/homebrew然后清理配置文件里的brew shellenv相关行重新安装即可。这里再次强调不要顺手sudo rm -rf /opt/opt是系统目录里面可能还有其他东西。只删/opt/homebrew这一个目录就好。权限类报错也经常和残留有关比如No such file or directory rb_sysopen - /opt/homebrew/...、Permission denied。常见原因是目录属主不对。标准处理sudo chown -R $USER:admin /opt/homebrew自定义目录同理确保~/Develop/Tools目录属于你自己而不是之前用 root 创建留下的。6.4 brew install redis 等软件时的联动报错很多人装 redis 也会踩到 Homebrew 的关联坑。最典型的是两个一是装完以后redis-cli还是找不到。这其实不是 redis 的问题是 brew 的 bin 目录没进 PATH或者 shell 缓存了旧命令。用which redis-cli检查路径再用brew services start redis启动服务然后用redis-cli ping验证能返回PONG就说明环境正常。二是编译安装失败报各种make、cc、xcrun错误。这类问题绝大部分是 Xcode Command Line Tools 缺失或路径异常执行xcode-select --install # 或者修复路径 sudo xcode-select --reset sudo xcode-select -s /Library/Developer/CommandLineTools另外一个隐蔽情况如果你不小心在 Rosetta 终端里执行了brew install redis装出来的可能是 x86_64 版本跑起来性能差且和系统的 arm64 生态割裂。所以每当你安装任何软件时先确认uname -m是arm64再动手。7. 配置完成后的健康检查与日常维护习惯7.1 brew doctor、brew config 和 brew --prefix 三个验证入口环境配好、安装成功之后建议养成每次重装环境后体检的习惯。三个命令就能看出大问题brew doctor brew config brew --prefixbrew doctor会列出当前 Homebrew 环境里它认为有隐患的地方比如目录权限问题、路径冲突、多余环境变量等。刚装完基本是干净的如果提示Warning按提示修一遍再进入下一步。brew config会输出编译环境信息重点看这几行HOMEBREW_PREFIX前缀标准路径是/opt/homebrew自定义路径是你的目标目录HOMEBREW_BOTTLE_DOMAIN是否指向镜像macOS版本和CLT版本是否满足要求7.2 常用维护命令与 Brewfile 备份迁移日常维护其实不需要太操心几条常用命令记住就好brew update # 更新 brew 自身和 formula 索引 brew upgrade # 升级所有已安装的软件包 brew cleanup # 清理旧版本缓存 brew autoremove # 自动移除不再需要的依赖 brew services list # 查看后台服务状态换机器或备份环境时强烈建议用 Brewfile。在旧机器上执行cd ~/Develop brew bundle dump --file~/Brewfile新机器装好 Homebrew 以后执行brew bundle install --file~/Brewfile它会把之前所有通过 brew 安装的 formula 和 cask 自动装回来。这个习惯能让你从手动重装一百个软件的痛苦里解放出来。自定义路径~/Develop/Tools/Homebrew时备份逻辑也简单brew bundle dump生成的清单记录了所有包名配合统一的~/Develop目录整体 rsync恢复环境非常快。7.3 最后一个小技巧优先用 brew shellenv 而不是手写环境变量再说一个实际维护心得不管你在哪篇教程里看到让人手动export PATH/opt/homebrew/bin:$PATH的写法你都可以用eval $(/opt/homebrew/bin/brew shellenv)代替。两者在 Apple Silicon 默认路径下效果几乎一样但前者少设了MANPATH和INFOPATH后者更接近官方维护的姿态。自定义路径也一样eval $($HOME/Develop/Tools/Homebrew/bin/brew shellenv)这句对标准路径、自定义路径都通用是我个人最推荐的环境变量配法。如果你在配置过程中发现brew --prefix输出的目录和预期不一致优先检查brew可执行文件的位置而不是去折腾环境变量。因为 Homebrew 的一切路径逻辑都是从那个bin/brew倒推出来的。记住这一点后面遇到再奇怪的路径问题你都有了一个稳定的排查锚点。
返回列表