Nushell 0.117.0 于 2026 年 11 月 9 日发布,这是该项目近期最重磅的一次更新。这个用 Rust 编写的 Shell 目前在 GitHub 上约有 4.1 万星,采用 MIT 许可证,而这次的特性列表终于让它兑现了最初的承诺:**别再解析文本,开始直接管道传递数据**。
本次发布的大部分重头戏都来自 Nushell 维护者 @fdncred,而贯穿其中的主题高度一致——让程序之间传递带类型的记录(record),而不是被压平成一行的字符串。

*图:AI 生成的插图。写作时没能拿到该版本发布的真实横幅截图。*
## Nushell 一直以来的痛点
Nushell 是一个结构化 Shell。它的内置命令返回的不是文本行,而是记录的表格,你可以用真正的运算符来过滤、排序、重塑:
“`nu
ls | where size > 1kb | sort-by modified | select name size
“`
问题出在你一旦离开 Shell 内部。把 `ls` 管道给 `grep`,运行 `docker ps`,或者调用 `git status`,你就又回到了盯着人类可读输出、用 `awk` 或 `sed` 去解析的状态。Nushell 内部数据模型与外部凌乱世界之间的这道裂缝,正是大多数摩擦——以及大部分 Shell 脚本工作——的所在。
## 少解析,多管道:@structured 属性
0.117.0 的头号特性直接补上了这道裂缝。新的 `@structured` 属性允许你教外部命令:当它被管道进 Nushell 时如何产出结构化数据,而它单独运行时的样子保持不变:
“`nu
@structured [ –porcelain ] {
lines | parse –regex ‘^(?
} extern “git status” [] git status
git status | where status =~ M | get file
“`
仔细看这段。单独运行 `git status` 仍然给你 Git 熟悉的人类可读输出。但只要把它管道进另一个 Nushell 命令,Shell 就会透明地加上 `–porcelain`、解析响应,并把带命名字段的记录交给你。`where status =~ M` 现在过滤的是一个有类型的列,而不是一个 `grep` 正则;`get file` 则把文件名取出来。
这是本次发布中影响最深的一项改动。这意味着大量既有的 Shell 脚本——人们为 `docker`、`kubectl`、`gh`、`systemctl` 写的那些 `grep | awk | cut` 链——现在可以改写成普通的类型化管道。

*图:AI 生成的结构化数据管道概念插图。写作时没能拿到真实截图。*
## REPL 快了一步
0.117.0 还新增了一条实时状态栏,显示在你正在输入的内容下方。在按下回车之前,Shell 就能告诉你:
– 当前输入里的第一个解析错误或类型错误
– 命令签名,并高亮当前所处的参数
– 你正在构建的管道的输出类型
– 对于完全由常量命令组成的表达式,还会给出结果预览
输入 `”a,b” | split row “,”`,你会在回车之前看到一个小小的 `= [a, b]` 预告。它还没替你把活干完,但已经很接近了。你可以通过 `$env.config.status_line` 决定显示哪些部分,`enable`、`errors`、`signature`、`type`、`preview` 都有独立开关。
一个贴心的细节:`Ctrl+C` 现在可以中断超大规模的数学归约。`1..10000000000 | math sum` 会报 `Operation interrupted`,而不是永远跑下去。
## 不用手写的补全
新的 `generate completions` 命令可以从你大概率已经拥有的来源生成 Nushell 的 `extern` 定义与补全器:
– carapace 规格文件
– fish 补全
– zsh 补全
– man 页面
– `–help` 输出
“`nu
generate completions git
| save ($nu.user-autoload-dirs | first | path join git-completions.nu)
“`
把该模块存进 autoload 目录,Tab 键就能补全 Git 的子命令、分支名、远程仓库和标签。来源优先级依次是 carapace、fish、zsh、man、help,`–source all` 可以合并使用。对于拥有原生 Cobra 补全的工具,比如 `gh`、`docker` 和 `kubectl`,Nushell 同样能把它们接进来。

*图:Nushell 官方演示动画帧,取自项目仓库。*
## 跳过 transpose 往返的记录命令
四个新命令可以直接操作记录,无需先转成表格再转回来:
– `record where` —— 按值或键过滤字段
– `record each` —— 一次遍历同时变换键和值
– `record walk` —— 递归遍历嵌套数据并按单元格路径脱敏
– `record apply` —— 为不同字段套用不同的类型转换
其中最值得立刻抄走的是 `record walk` 的脱敏场景。现在要给嵌套配置里的密钥打码,不必再手写递归函数:
“`nu
{ db: { host: prod, password: hunter2 }, api: { token: abc } }
| record walk {
|value, path|
if ($path | to text) =~ ‘(?i)secret|token|password’ {
‘***’
} else {
$value
}
}
“`
`password` 和 `token` 都会变成 `***`,而 `host` 仍然是 `prod`。`record apply` 则用同样的思路做类型转换:
“`nu
{ age: ’42’, email: ‘A@B.COM’ }
| record apply { age: { into int }, email: { str lowercase } }
“`
这些命令同样接受表格,并按行流式处理,所以 `record apply` 可以当作按列转换类型的工具来用。

*图:Nushell 官方截图 —— `ls` 输出本就是结构化数据,而不是文本。*
## .env 支持,终于来了
项目里那份 `.env` 文件终于不需要自己写解析器了:
“`nu
open .env | load-env
“`
支持的格式包括 `.env`、`.env.local` 和 `prod.env`。带引号的值、多行值、注释、可选的 `export` 以及变量展开都能正常工作——关键是,文件里的任何内容都不会被当作命令执行。环境变量转换也会在加载变量时自动完成,所以字符串形式的 `PATH` 会直接变成列表,不用再额外做转换。
## 可复用的类型别名
如果你曾经把同一份庞大的记录类型复制粘贴进三个命令签名里,0.117.0 让你可以给它起个名字:
“`nu
type Point = record
type Polygon = list
def first-x [ poly: Polygon ]: nothing -> float { $poly.0.x }
“`
别名可以引用其他别名,在泛型类型内部同样成立,还可以用 `export type` 从模块导出。自定义类型(包括来自插件的类型)现在也能在 Nushell LSP 里正确解析。
## 会破坏什么,以及如何迁移
这次发布的破坏性变更是实打实的。如果你已经在脚本或配置里使用 Nushell,下面这几条最可能咬人:
**`type` 现在是保留关键字。** 任何以该名字定义过的命令、别名或 `extern` 都需要改名。要调用名为 `type` 的外部命令,请用 `^type`;要在 Nushell 内部查找命令,请用 `which`。
**`ps` 的列布局变了。** `ps` 现在能在所有平台上列出全部进程,包括 macOS 和 Windows。`user` 列现在出现在普通 `ps` 中,`virtual` 只在 `ps -l` 下可用,`process_threads` 更名为 `threads`。`working` 和 `paged` 两列被移除——想看进程独占的内存请用 `private`。由于出现了新的空值,排序前要先剔除它们:
“`nu
ps | compact cpu | sort-by cpu | last 3
“`
**键位绑定和菜单列表现在是整体替换,而不是追加。** 给 `$env.config.keybindings = […]` 或 `$env.config.menus = […]` 赋值,会替换整个列表,包括 Nushell 的默认菜单以及打开它们的按键(Tab 与 Shift+Tab 补全、Ctrl+Space、F1 帮助、Ctrl+X 与 Ctrl+Z 分页、Ctrl+R 历史菜单)。要追加请改用 `++=`:
“`nu
$env.config.keybindings = (config default).keybindings
$env.config.keybindings ++= [
{ name: my_binding, modifier: control, keycode: char_t, mode: emacs, event: { send: clearscreen } }
]
“`
**`group-by –to-table` 保留键的原始类型。** 分组键不再被转换成显示字符串,因此 `1 MB` 与 `1.001 MB` 现在是两个独立的分组,而不是合并成一个。假设键是字符串的脚本需要更新。
**运算符优先级变了。** `not` 现在作用于整个比较表达式:`not $a == $b` 等价于 `not ($a == $b)`。此前它只作用于紧随其后的那个值。
**带点的模块目录会保留完整名字。** `use a.b` 现在创建模块 `a.b`(命令形如 `a.b foo`),而不再是 `a`。
## 性能优化
Nushell 解析真实脚本的速度现在快了约 **1.33 倍**,这缩短了启动时间和脚本加载时间。在 Apple 芯片 Mac 上,每个进程少用约 **3 MiB** 内存。默认配置下的启动耗时从约 15.70 毫秒降到 13.10 毫秒出首个提示符,首个提示符前回收的指令数也从 149.3M 降到 119.8M。调用 `each`、`where` 或 `update` 的闭包运行速度也快了不少。
## 安装 0.117.0
Linux、macOS 和 Windows 都有预编译二进制包,在 GitHub 发布页下载。Debian 与 Ubuntu 上项目自建了 apt 仓库:
“`sh
wget -qO- https://apt.fury.io/nushell/gpg.key | sudo gpg –dearmor -o /etc/apt/keyrings/fury-nushell.gpg
echo “deb [signed-by=/etc/apt/keyrings/fury-nushell.gpg] https://apt.fury.io/nushell/ /”
| sudo tee /etc/apt/sources.list.d/fury-nushell.list
sudo apt update
sudo apt install nushell
“`
Fedora、Rocky Linux 和 RHEL 使用 `https://yum.fury.io/nushell/` 上的 yum 仓库。Alpine 需要把 `https://alpine.fury.io/nushell/` 加进 `/etc/apk/repositories`。Homebrew 用户可以执行 `brew install nushell`,也有 `npm install -g nushell` 这条路,但 npm 版本不包含插件。
容器镜像发布在 GitHub Container Registry,提供 Alpine 和 Debian 两个基础版本:
“`sh
docker run -it –rm ghcr.io/nushell/nushell:latest-alpine
“`
从源码构建需要 Rust 工具链(1.96.1 或更高版本)、C 编译器和 `pkg-config`。自 Nushell 0.105 起,默认使用 `rustls` 取代 OpenSSL,只有在启用 `native-tls` 特性时才需要 OpenSSL 开发头文件。

*图:Nushell 官方截图 —— `http get` 返回结构化响应,可以直接查询。*
## 结论
Nushell 0.117.0 是让”结构化 Shell”这个论点真正站得住脚的一次发布。在它之前,转向 Nushell 意味着放弃 `grep`、`awk` 以及你依赖的所有文本处理工具。有了 `@structured`、`generate completions` 和这一批记录命令,外部那些无类型的程序终于会向类型化管道输送数据,而不是反过来。
破坏性变更是真实存在的,尤其是 `type` 关键字被保留、以及键位绑定从”追加”变成”整体替换”这两条。如果你已有 Nushell 配置,升级前请先读一遍发布说明,并做好备份。
项目地址在 [github.com/nushell/nushell](https://github.com/nushell/nushell),MIT 许可证,约 4.1 万星,完整发布说明见 [nushell.sh/blog](https://www.nushell.sh/blog/2026-11-09-nushell_v0_117_0.html)。

*图:Nushell 官方截图 —— 错误以详细的 miette 报告呈现,而不是一句干巴巴的提示。*
评论