最近在一台刚到手的新 MacBook 上配置开发环境,跑项目依赖时第一行命令就给我一个下马威:ruby 2.6.10已经终止支持,一堆 gem 装不上,而系统里自带的 Ruby 版本比社区主流整整落后了好几年。这个场景我相信很多 macOS 开发者都遇到过。很多人想升级 macOS 上的 Ruby 版本,但打开教程发现方案五花八门,什么rvm、rbenv、brew install ruby,还没动手就先晕了。这篇文章就是把“升级 Ruby 版本”这件事彻底讲透,从方案选型、环境搭建、具体命令到常见坑位排查,一条龙梳理清楚,适合刚接触 Ruby 或者被系统版本坑过的人直接照着做。
1. 升级前先想清楚:为什么 macOS 上 Ruby 版本这么难搞
1.1 系统自带 Ruby 的几个大坑
macOS 虽然自带 Ruby,但这个 Ruby 并不是给开发者准备的。它主要是给系统脚本、自带工具链用的,所以版本更新非常保守。比如 macOS Monterey 自带的 Ruby 是 2.6.8,到了 Sonoma、Sequoia 之后虽然版本有所提升,但依然跟不上 Ruby 官方每年一个大版本的节奏。而 Rails 7/8、Jekyll、Fastlane 这些常用工具,对 Ruby 版本的要求越来越高,新版本 gem 普遍要求 Ruby 3.0 以上,用系统 Ruby 去跑,基本就是寸步难行。
更麻烦的是,系统 Ruby 被系统完整性保护(SIP)锁得死死的。即使你想用sudo gem update --system强行升级,也会发现要么权限不足,要么直接改坏了系统依赖。我见过有人硬改/usr/bin/ruby,最后系统里一堆工具报错,只能重装系统。所以第一条原则就是:永远不要试图去升级 macOS 自带的 Ruby,你真正要做的是在系统里装一个独立的、由你控制的 Ruby 环境。
1.2 升级方案怎么选:rbenv、RVM、Homebrew 到底用哪个
升级 Ruby 的常规方案其实就三个:Homebrew 直接装、RVM、rbenv。我用一张表把它们的区别列出来,看得更清楚。
| 方案 | 版本管理 | 项目级切换 | 上手难度 | 系统侵入性 | 推荐度 |
|---|---|---|---|---|---|
| Homebrew 直接安装 | 只装一个最新版 | 不支持 | 最低 | 中 | 偏低 |
| RVM | 支持多版本 | 支持,但较重 | 中 | 高 | 一般 |
| rbenv | 支持多版本 | 非常轻量 | 低 | 低 | 强烈推荐 |
Homebrew 直接brew install ruby是最省事的,装完就是一个新版本 Ruby,简单粗暴。但它只能维护一个版本,如果你同时有一个老项目要 Ruby 2.7,一个新项目要 Ruby 3.3,它就没法满足需求了。RVM 功能很强大,安装卸载、切换、gemset 都有,但它会在 shell 里注入自己的函数,运行时会有些“重”,在团队协作中容易带来环境差异。rbenv 是我最推荐的方式,它本质上只做一件事:通过 shim 机制改变ruby命令的指向。不同的目录可以绑定不同版本的 Ruby,切换成本几乎为零,而且完全不碰系统 Ruby,安全干净。Rails 官方文档也明确推荐使用 rbenv,这也是我下面所有实操步骤的基础。
2. 环境准备与工具链搭建
2.1 先装好 Homebrew
在 macOS 上装 rbenv,最省事的方式是通过 Homebrew。所以不管你有没有,先检查一下自己电脑上是否已经安装了 Homebrew。打开终端输入:
brew -v如果返回版本号,说明已经装好了。如果没有,就执行 Homebrew 官方安装脚本:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"这个脚本会自动安装 Xcode Command Line Tools,可能会弹出一个窗口让你确认。这个过程需要几分钟甚至更久,取决于网络状况。装完之后建议执行brew doctor确认一下环境是否健康。如果brew doctor提示有警告,比如路径不对、目录权限有问题,先处理掉再继续,否则后面装 rbenv 会遇到各种奇怪的问题。
2.2 安装并初始化 rbenv
Homebrew 就绪后,安装 rbenv 本身其实只有一条命令:
brew install rbenv ruby-build这里有两个组件:rbenv负责版本管理,ruby-build负责把指定版本的 Ruby 源码下载下来并编译安装。很多教程只写了brew install rbenv,导致后面执行rbenv install的时候提示找不到命令,所以记得把ruby-build一起装上。
装好之后需要初始化 rbenv,不过先不要急,初始化要和 shell 配置一起做,下面展开。这里可以先验证一下 rbenv 是否安装成功:
rbenv version如果输出system,说明 rbenv 已经认识到了当前用的是系统 Ruby。
2.3 把 rbenv 正确挂进 shell
rbenv 的原理是在PATH环境变量前面插入一个 shims 目录,当你在终端输入ruby时,实际先执行的是 shims 里的小脚本,它会根据当前目录的版本配置,把命令转发给对应版本的 Ruby。所以要实现这个机制,必须在 shell 启动时执行eval "$(rbenv init -)"。
macOS 默认 shell 是 zsh,所以把这一行加到~/.zshrc里:
echo 'eval "$(rbenv init -)"' >> ~/.zshrc如果你用的是 bash,则加到~/.bash_profile:
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile改完之后执行source ~/.zshrc让配置立刻生效,或者干脆关掉终端重新开一个。然后验证一下:
which ruby正常情况下,你会看到输出路径是/Users/你的用户名/.rbenv/shims/ruby,而不是/usr/bin/ruby。如果显示的还是系统路径,说明 rbenv 没有生效,先别往下走,回头检查配置是否写对、是否 source 了正确文件。
3. 核心实操:用 rbenv 安装并切换 Ruby 版本
3.1 查看可用版本和当前版本
环境搭好之后,先看看 rbenv 能装哪些 Ruby 版本:
rbenv install -l这个列表很长,全是版本号,从 Ruby 1.8 到最新版都有。我一般会先筛一下稳定版:
rbenv install -l | grep -E "^\s*3\.[234]\.[0-9]+$"这样能看到当前可安装的 3.2、3.3、3.4 系列版本。选版本的时候要结合你的项目实际情况。如果是新项目,直接用最新稳定版;如果是老项目,看看Gemfile顶部有没有ruby 'x.x.x'的声明,尽量匹配。同时也可以用ruby -v看一眼当前生效的版本,方便后面对比。
3.2 安装指定版本并设置为全局版本
确定好版本号之后,安装命令是:
rbenv install 3.3.5这里的3.3.5可以替换成你需要的版本号。这个命令会从源码编译 Ruby,耗时较长,一般来说 5 到 15 分钟都是正常的。编译期间终端会有大量输出,不要一看滚动就以为卡住了,只要不是长时间完全静止,就耐心等着。
装完之后要让这个版本变成全局默认版本:
rbenv global 3.3.5然后执行rbenv rehash,重新生成 shims 脚本,让新的 Ruby 版本被正确接管。验证一下:
ruby -v如果输出ruby 3.3.5p...,并且which ruby指向~/.rbenv/shims/ruby,说明升级成功。这时候如果再执行rbenv versions,就能看到列表里既有system又有3.3.5,当前版本前会带一个星号。
3.3 项目级版本管理与 .ruby-version 文件
全局版本解决的是“默认用哪个 Ruby”的问题,但实际开发中经常同一个电脑上不同项目要求不同的 Ruby。rbenv 最方便的地方就在这里:你可以进入某个项目目录,单独指定一个版本。
cd ~/work/legacy_project rbenv local 3.3.5这条命令会在当前目录生成一个名为.ruby-version的文件,里面只有一行版本号。之后只要你在这个目录及其子目录下打开终端,rbenv 就会自动切换成.ruby-version指定的版本,离开这个目录则回到全局版本。
这个.ruby-version文件建议提交到 Git 仓库里,这样团队所有人进入项目后用rbenv就能自动切换到相同版本,避免“我本地能跑,你本地跑不了”的扯皮问题。如果项目里用了 asdf 这种多语言版本管理工具,也可以兼容相同格式的.tool-versions。
3.4 gem 与 bundler 的配套设置
Ruby 版本切换好了,gem 的安装路径也跟着变了。因为 rbenv 管理的 Ruby 装在用户目录下,所以gem install不再需要sudo,也没有系统目录权限问题。你可以先确认一下 gem 的环境:
gem env home正常情况下这个路径会在.rbenv/versions/3.3.5/lib/ruby/gems/...下面,而不是系统目录。如果你的 gem 源在国内,建议把源换成 Ruby China 的镜像:
gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/然后安装 bundler:
gem install bundler后续在项目里执行bundle install,所有 gem 都会安装到当前 Ruby 版本对应的 gems 目录,互不干扰。记住,只要是通过 rbenv 管理的 Ruby,所有 gem 命令都不需要加sudo,一旦你发现自己又需要sudo了,那多半是因为命令走回了系统 Ruby,赶紧检查一下which gem。
4. 升级路上的常见问题与排查技巧
4.1 编译失败 / 缺少依赖
用rbenv install安装 Ruby 时最常遇到的就是编译失败,报错信息五花八门,但核心问题基本都是缺少依赖库。常见的有:
checking for openssl/ssl.h... no或者:
checking for libyaml... no这是因为 ruby-build 在编译时依赖 OpenSSL、Readline、LibYAML、zlib 等库。解决办法是先把这些依赖装全:
brew install openssl readline libyaml zlib装完之后仍然保留旧的编译命令,最好设置一下环境变量,让编译器能找到这些库。以 OpenSSL 为例:
export RUBY_CONFIGURE_OPTS="--with-openssl-dir=$(brew --prefix openssl)"然后重新执行rbenv install。如果报错信息里出现Unknown option: --with-openssl-dir,说明 ruby-build 版本太旧,先执行brew upgrade ruby-build再试。不同 macOS 版本和不同 Ruby 版本组合,具体依赖可能略有差异,但“把 brew 依赖装全 + 指定 openssl 路径”这一套组合拳能解决大多数编译问题。
4.2 切换后 ruby -v 没变化 / 命令找不到
这是一类特别让人抓狂的问题:明明执行了rbenv global 3.3.5,ruby -v却还是老的。出现这种问题,先按顺序排查。
第一步,检查which ruby。如果路径不是~/.rbenv/shims/ruby,说明 rbenv init 没生效或者 PATH 顺序不对。重新执行source ~/.zshrc,再看看echo $PATH里~/.rbenv/shims是不是排在最前面。
第二步,如果which ruby指向 shims,但版本没变,很可能是 shell 缓存了旧命令路径,执行:
hash -r然后重新运行ruby -v。
第三步,如果你改变过~/.zshrc,但新开的终端依然不生效,检查一下终端是不是登录 shell。macOS 上使用 bash 的话,配置要写进~/.bash_profile而不是~/.bashrc,因为 bash 作为登录 shell 只加载前者,这是很多人踩过坑的地方。
4.3 gem 装在系统目录 / 权限报错
执行gem install时如果出现permission denied @ rb_sysopen这类报错,大概率是当前 Ruby 还是系统版。rbenv 管理的 Ruby 位于用户目录,写入权限完全没问题,根本不需要 sudo,也不会出现权限错误。
遇到权限问题,第一步还是看which ruby和which gem。如果两个都不在 rbenv 的 shims 路径下,说明 shell 环境没初始化好,回到 4.2 排查。如果ruby在 rbenv 下而gem不在,可以用rbenv rehash重新生成 gem 命令的 shims,因为安装新 Ruby 后 gem 命令可能没有及时生成。
另外一个常见误区是直接执行sudo gem install,这会把 gem 装到系统 Ruby 目录,污染系统环境,也会让版本管理彻底混乱。记住了,rbenv 环境里永远不需要 sudo。
4.4 疑难杂症速查表
下面这张表是我实际工作中遇到过的几个高频问题,直接对照参考即可。
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
rbenv: version 'x.x.x' is not installed | .ruby-version指定了一个没装过的版本 | rbenv install x.x.x安装对应版本 |
rbenv install下载源码时 404 | ruby-build 版本太旧,不认识新版本 | brew upgrade ruby-build |
require cannot load such file -- openssl | 编译时没链接 OpenSSL | 设置RUBY_CONFIGURE_OPTS后重装 Ruby |
bundle exec rails找不到命令 | 依赖没装到当前 gem 环境 | 执行bundle install,优先用bundle exec |
rbenv: command not found | Homebrew 路径不在 PATH 中,或没执行 init | 检查 shell 配置和 PATH |
| Ruby 版本切换成功,但 gem 路径没变 | 没有执行rbenv rehash | 执行rbenv rehash,重开终端 |
5. 升级完成后的验证与日常使用建议
5.1 如何确认升级成功
升级不能被一条ruby -v就糊弄过去,我建议你做一次完整的验证,确认当前 Ruby 来自 rbenv 管理目录,而不是系统目录。在终端依次执行下面几条命令:
which ruby ruby -v which gem gem -v gem env home rbenv versions我预期的结果大概是这样的:
/Users/你的用户名/.rbenv/shims/ruby ruby 3.3.5p100 (2024-09-03 revision 1c634b6a0d) [arm64-darwin23] /Users/你的用户名/.rbenv/shims/gem 3.5.16 /Users/你的用户名/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0 system * 3.3.5 (set by /Users/你的用户名/.rbenv/version)只要which ruby出现在.rbenv/shims下,并且gem env home指向.rbenv/versions里的目录,就说明升级是成功的。如果你看到/usr/bin/ruby,说明前面有一步没做对,别急着往下,先回第 2 章查配置。
5.2 日常升级 Ruby 版本的操作路径
rbenv 装好后,以后 Ruby 出了新版本,升级流程其实已经形成了一个固定套路。想查看当前可安装的新版本,执行rbenv install -l,选好版本号后用rbenv install安装,装完rbenv global切换全局版本,再rbenv rehash。如果是针对某个项目升级,就进入项目目录执行rbenv local,并更新.ruby-version。
升级大版本号时,比如从 3.2 升到 3.3,需要额外注意Gemfile里声明的 Ruby 版本约束,以及一些编译型 gem 是否需要重新编译。我的习惯是升级前先bundle install保存当前锁文件,升级后再重新执行bundle install,有报错就根据报错逐个处理。每升一个版本,我会先在测试环境跑一遍完整的测试用例,确认没问题再切换到日常开发。
5.3 一些实用的小技巧和避坑心得
文章最后分享几个我实际操作中总结出来的技巧。
第一,在项目里一定提交.ruby-version文件。哪怕你是单人开发,半年后回头看自己的项目,也会忘记当时用的哪个 Ruby 版本,有文件在就不用靠猜。
第二,不要同时用 rvm 和 rbenv,二者会在 shell 里互相打架。如果之前装过 rvm,需要先彻底卸载,再装 rbenv,否则会出现各种莫名其妙的命令覆盖问题。
第三,rbenv global设置的是一个全局兜底版本,如果你在某个目录下发现执行的 Ruby 版本不对,先看看当前目录有没有.ruby-version,有没有被它“绑定”成了一个旧版本。排查顺序永远是:命令行里输入的which ruby、当前目录的.ruby-version、全局的rbenv version。
第四,如果团队已经用了 Docker 开发环境,Ruby 版本的管理其实可以做成镜像的一部分,本机只需要一个足够新的 Ruby 跑基础脚本就行。但当你想在本地快速验证某个 gem 或一段脚本时,rbenv 的轻量灵活还是无可替代。我至今在任何 macOS 开发机上都会先装 rbenv,因为它是少有的、配置一次就能用很多年还不用怎么操心版本管理工具。