GitHub 项目 amatsuda/rubish 做了一件很绕、也很有意思的事:用纯 Ruby 写一个 Unix shell。

它不是把 Ruby 当脚本语言塞进 shell,而是把 shell 语法解析、编译成 Ruby 代码,再交给 Ruby VM 执行。项目采用 MIT 许可证。README 里写得很满:目标是 Fully Bash-compatible,现有 Bash 脚本无需修改即可运行;不兼容会被视为 bug。

这句话要拆开看。

“兼容 Bash”目前是项目方目标和维护态度,不是第三方已经验证过的成熟结论。Rubish 的价值也不在“又来了一个 shell”。真正值得看的,是它试图把 Bash 兼容层、Ruby 语法和 Ruby 程序内嵌 API 放进同一个执行环境。

Rubish 不是换壳,变化在执行路径

Rubish 可以作为交互 shell 使用,也支持 rubish -c 执行单条命令、运行脚本,甚至作为 login shell。它还提供 Ruby 程序内嵌 API,比如创建 Rubish::REPL,调用 tokenize、parse、completion、prompt segments 等接口。

这意味着它面向的不只是终端用户。终端模拟器、IDE 插件、GUI 前端也可以在 Ruby 进程里驱动一段 shell 会话。

核心差异可以压成一张表:

对比项Bash / Zsh 常规路线Rubish 路线现实判断
执行机制shell 自身解释执行shell 语法编译成 Ruby 代码,由 Ruby VM 执行Ruby 集成更顺,但兼容压力更大
脚本目标Bash/POSIX 生态项目方声称 Bash 全特性兼容只能先按项目声明理解,不能当成生产验证
扩展方式shell 函数、插件、补全脚本Ruby 条件、块、lambda、内联 Ruby、方法链Ruby 用户更自然,普通 shell 用户要重新适应
嵌入能力多靠进程、文本协议、外部调用Ruby 进程内 API更适合工具作者做实验

所以,Rubish 的主线不是“我要打败 Bash”。

更准确的说法是:它想在 Ruby 运行时里做一个 Bash 兼容执行层。这个定位窄一些,但也更有辨识度。

对 Ruby 开发者来说,这会减少一类切换成本。日常在 Rails、Bundler、rbenv、Ruby 脚本和 shell one-liner 之间来回跳的人,最容易理解它的吸引力。

对团队来说,动作应该相反:不要急着迁移默认 shell。可以拿个人脚本、项目内工具、非关键 CI 辅助脚本试一圈,但别把它直接放到生产发布链路里承担 Bash 替代职责。

它比 Bash 多出的,是 Ruby 用户熟悉的写法

Rubish 最强的标签是深度 Ruby 集成。

它允许在 ifwhileuntil 中用 { } 包裹 Ruby 表达式当条件。命令可以写成 ls('-la') 这种方法调用。管道可以写成 ls().sort.uniq。命令输出也能接 .each.map.select 这类 Ruby 迭代块,按行处理。

它还支持内联 Ruby、Ruby 数组和正则字面量、-> { } lambda、Ruby 风格 def...end 函数定义,以及用 Ruby 函数生成 prompt。

这些能力不是为了让 shell 语法更花。它解决的是一个老问题:很多命令行任务本来就夹在 shell、awk、sed、Ruby one-liner 之间。Rubish 试图把这些工作收进一个语法空间里。

举个判断层面的差别:

使用者现在更适合怎么做不建议怎么做
Ruby 开发者作为交互 shell、个人脚本执行器、项目本地 .rubishrc 试用直接替换团队默认 shell
终端 / IDE 工具作者研究它的解析、补全、prompt、REPL API只把它当另一个命令解释器看
普通 Bash 用户观望兼容性测试和边界案例为了新语法立刻迁移

还有一个很现实的功能是 lazy_load。它可以把 rbenv、nvm、pyenv 这类较慢初始化放到后台线程处理,并在下一次 prompt 前应用结果。

这点不玄。很多开发者的 shell 启动慢,并不是 shell 本身多慢,而是版本管理器、补全脚本、环境初始化一层层叠上去。Rubish 至少把这个痛点摆到了台面上。

真正的门槛是兼容性和安全边界

Ruby 集成提高表达力,也会扩大风险面。

如果一个脚本里可以混入内联 Ruby、lambda、块和 Ruby 条件,它就不再只是传统 shell 脚本。执行不可信脚本时,风险模型也变了。

Rubish 提供 rubish -r restricted mode,用来关闭 Ruby 集成功能,只允许标准 shell 语法。被关闭的能力包括内联 Ruby、lambda、块、Ruby 条件和数组字面量。

这个设计很关键。它说明作者知道问题在哪里:强表达能力不能默认带进所有脚本场景。

但 restricted mode 也需要单独验证。它是否足够隔离 Ruby 能力?边界案例怎么处理?复杂 Bash 行为、子进程控制、信号、补全、长时间交互和性能表现能不能稳定?这些目前都不能靠 README 一句话解决。

接下来最该看的不是 star 数,也不是示例语法多漂亮,而是三件事:

  • Bash 兼容测试覆盖到什么程度,尤其是复杂脚本和历史边角行为。
  • rubish -r 是否能清楚隔离 Ruby 集成功能,适合执行不可信脚本。
  • 交互使用时,子进程、信号、补全、prompt、启动速度和长时间运行是否可靠。

Shell 是低层日用品。它可以不性感,但不能经常出错。一次脚本行为不一致,就足够让人退回老工具。

Rubish 现在最合适的位置,是给 Ruby 开发者和工具作者一个可试验的新执行环境。它如果真能站住,靠的也不会是“语法好看”,而是 Bash 兼容、Ruby 集成和安全模式三件事能同时过关。

这很难。

也正因为难,它才比普通的“新 shell 项目”更值得看一眼。