在 Windows 上做终端开发,最烦人的从来不是终端本身,而是终端里那套跟 Unix 世界长期割裂的命令体验。早几年我从 Linux 切回 Windows 办公,每次打开 PowerShell 想复现一套ls | grep | sort的管道操作,都要先愣一下:参数怎么写来着?昨天还能用的历史命令为什么今天就报错了?这类摩擦积累多了,就会让人怀疑是不是自己不够适应。
后来我把 Nushell、coreutils 和 Fresh 这三样组合起来装进 Windows Terminal,整个命令行体验才算稳定下来。Nushell 负责把文件、目录、环境变量都变成结构化数据,coreutils 补上 Windows 缺失的 Unix 基础命令,Fresh 则把所有配置文件收进一个版本仓库,换机器也不用从头折腾。这篇文章就把我实际配置和使用的完整过程写出来,包含大量踩坑记录和命令示例,适合想提升 Windows 终端开发体验、又不想整天跟 PowerShell 语法死磕的开发者参考。
1. 这套组合到底解决了 Windows 终端的什么深层次问题
1.1 不是"换壳",是换掉整套命令思维
很多人以为 Windows 的终端问题只出在 CMD 太老太丑,换一个 Windows Terminal 皮肤就好了。其实 Windows Terminal 只是壳,真正让人难受的是 shell 本身。CMD 的批处理语法早就落后,PowerShell 虽然强大,但命令设计更接近"面向对象编程"而不是"命令行艺术",Get-ChildItem -Recurse -Filter *.log | Where-Object { $_.Length -gt 10MB }这种长句子,敲一两次还行,每天敲几十次就很折磨。
把 Nushell 装进来之后,最直观的改变是:命令短了,输出统一了。Nushell 里查看当前目录文件就是ls,过滤就是where,排序就是sort-by,虽然和传统 Unix 命令不完全一样,但思路一致,而且输出天然是表格,不需要再额外培养理解ls -l各列含义的能力。
1.2 Nushell 不是又一个 shell,而是把文件当数据
Nushell 和其他 shell 最大的差别,在于它的管道里流动的不是纯文本字符串,而是结构化数据。你用ls拿到的不是一堆字符,而是一个包含文件名、大小、类型、权限的表;用open打开 JSON、CSV、TOML 文件,直接就是 Nu 的数据结构,可以用get、select、where继续操作。
这个特性的实际价值非常大。比如我想找出当前目录下最大的三个文件,Nushell 一句ls | sort-by size | reverse | first 3就行,不用先解析文本再写循环。在 PowerShell 里虽然也能做到,但代码量和思维负担完全不是一个级别。
1.3 coreutils:让 "没有 ls" 不再是笑话
Windows 上做开发最尴尬的场景,就是你明明记住了grep、sed、sort、wc这些命令的用法,结果打开 PowerShell 一个个都提示"无法识别"。解决方式不是把每个命令都找一遍 PowerShell 等价体,而是直接装一套 coreutils 的 Windows 移植版。
我这里用的是 uutils/coreutils,也就是用 Rust 重写的 GNU coreutils。它在 Windows 上原生编译,不需要装 Cygwin 或 WSL,装上之后ls -l、grep -r、kill -9、head -n 5这些肌肉记忆全部复活。虽然细节上还有兼容差异,但日常开发已经足够顺手。
1.4 Fresh 负责收尾:配置终于可以像代码一样维护
命令体验解决了,还有一个更隐蔽的痛点:配置文件分散在AppData、用户主目录、.config好几处,重装系统或者换电脑就要重新手工配一遍。Nushell 有自己的 history、env、config 文件,uutils/coreutils 有部分也会读环境变量,再加上 Windows Terminal 的settings.json,这些配置如果只在本地存在,迟早会丢一次。
Fresh 这类 dotfiles 管理工具就是把配置纳入 Git 仓库,用一个清单文件决定哪些内容要克隆、哪些文件要链接到哪个位置。它的目标不是创造一套复杂的新体系,而是让"配置"这件事可追踪、可回滚、可同步。标题里说的三件套,其实就是一个完整的解决方案:Nushell 提供工作逻辑,coreutils 提供命令工具,Fresh 把这些东西长期固化成你自己的开发环境。
2. 实操前的环境准备:Windows Terminal、字体与包管理三件套
2.1 Windows Terminal 的安装和为什么必须选它
既然标题已经说了是基于 Windows Terminal,那这一步基本没有悬念。我推荐直接用 winget 安装最新版,后续更新也方便:
winget install Microsoft.WindowsTerminal如果 winget 源里没有,也可以去 Microsoft Store 搜索安装。Windows Terminal 的好处不只是标题栏好看,而是它内置了多个终端配置(PowerShell、CMD、WSL 都能并存),支持标签页、分屏、自定义主题,还有比较关键的一点:它对 Unicode 和真彩色的支持比传统conhost好得多。Nushell 的表格渲染、颜色高亮,在旧控制台窗口里表现会大打折扣,所以这是必须换的底层。
2.2 安装 Nerd Font,Nushell 的表格才好看
Nushell 默认会在表格里用一些特殊字符来画边框,比如圆角线和符号,普通字体容易显示成方框。装一个 Nerd Font 能同时解决图标和字形问题。我用的是 JetBrainsMono Nerd Font,安装方式:
scoop bucket add nerd-fonts scoop install JetBrainsMono-NF装完打开 Windows Terminal 的设置,找到当前配置文件的字体栏,改成JetBrainsMono NFP。这一步不提前做的话,后面的表格输出会非常难看,而且容易误以为是 Nushell 渲染出了问题。字体问题排查起来很隐蔽,我建议在安装阶段就处理掉。
2.3 三个核心工具的推荐安装方式对比
Nushell、coreutils、Fresh 三者的安装走三个不同渠道,我分别列一下:
| 工具 | 安装方式 | 命令 |
|---|---|---|
| Nushell | winget 或 scoop | winget install Nushell.Nushell |
| uutils/coreutils | scoop 主仓库 | scoop install uutils-coreutils |
| Fresh | RubyGems | gem install fresh |
| Ruby(Fresh 依赖) | winget | winget install RubyInstallerTeam.RubyWithDevKit |
scoop 如果没有安装,可以用 PowerShell 执行irm get.scoop.sh | iex,日常装命令行工具会方便很多。Fresh 本身是 Ruby gem,所以要先装 Ruby 环境。如果你对 Ruby 无感,也可以用别的 dotfiles 管理工具代替,但既然标题单独点了 Fresh,我这里就以 fresh 为线索串下来。
2.4 装完先跑一次验证命令
装完不要急着配置,先验证几个东西:
nu --version coreutils --version gem list fresh需要注意,uutils/coreutils 装好后,可执行文件名字可能是coreutils,也可能是各个具体命令(ls、cat等),具体看 scoop shim 的安装方式。如果出现覆盖冲突,检查一下 PATH 顺序。Windows 上如果安装了 Git for Windows,它自带的部分 Unix 工具会在 PATH 里,可能存在ls.exe与 coreutils 的并存问题,一般用 scoop shim 目录的优先级来解决。
3. Nushell 落地配置:从第一行 config.nu 开始
3.1 找到配置文件位置并设置基础环境变量
第一次运行nu后,它会自动生成两个核心文件:config.nu和env.nu。在 Windows 上默认位置是:
%APPDATA%\nushell\config.nu %APPDATA%\nushell\env.nu不确定的话可以直接在 Nushell 里执行:
$nu.config-path $nu.env-path这两个文件就是整个 Nushell 行为的核心。我第一件要做的事是调整 PATH,让 scoop 的 shim 目录和.cargo/bin排在前面:
# env.nu 里追加 $env.PATH = ($env.PATH | prepend [ ($env.USERPROFILE | path join "scoop" "shims") ($env.USERPROFILE | path join ".cargo" "bin") ])Nushell 的 PATH 天然是数组,直接prepend就行。这个设计和传统export PATH=/xxx:$PATH本质上是一个思路,但可读性更好,也避免了 Windows 上经常出现的分号转义问题。
3.2 用 Nushell 内置命令还是外部 coreutils?
Nushell 自带了一套完整的内部命令,最常用的是ls、open、where、select、sort-by、into这类。这里的技巧是:凡是要继续接 Nu 管道处理的,优先用内部命令;凡是只做一次性处理、追求和 Linux 行为一致的,用外部 coreutils。
举个例子,ls内部命令的输出是结构化表格,可以直接ls | where size > 10kb,但它的输出列和 GNUls -l不完全一样,不能完全平滑替代所有脚本场景。如果你写出了ls -l | awk '{print $5}'这种传统命令,那就老老实实用外部 coreutils:
^ls -l | ^awk '{print $5}'Nushell 里用^前缀明确调用外部命令,避免和内部命令冲突。这个细节特别容易让人迷惑,我当时就是因为不知道^的作用,写ls一直进的是 Nu 内部命令,导致很多习惯性的参数完全失效。
3.3 配置别名:把高频命令压缩到指尖
Nushell 的别名语法和 bash 有点像,但有一点不同:它支持给外部命令起别名。我在config.nu里放了这样几行:
alias ll = ls -l alias grep = ^grep -n alias findf = ^fd --color=never alias ports = netstat -ano这里grep我刻意指向外部 coreutils,因为 Nu 内部没有完全等价的grep。而ll用的是 Nu 内部ls -l,两者规约不同但视觉输出差异不大,日常使用没有割裂感。
别名的核心原则是:不要让同一个词在不同 shell 里有完全不同的行为。如果你经常在 Git Bash 和 Nushell 之间切换,建议让别名尽量向 Linux 习惯靠拢,这样换过去不用改脑内映射。
3.4 主题和 Prompt:点到为止,不本末倒置
Nushell 支持use theme命令后,主题切换也方便了。但我个人的建议是:不要花太多时间折腾终端外观。颜色配置适度就好,重点要保证信息可读性,而不是七彩斑斓。Windows Terminal 本身已经提供了不错的配色,Nushell 默认表格在这个背景下很清晰,我基本只调整了两个地方:
- 关闭启动时的 logo 页,改成直接进入命令状态;
- 自定义 prompt,只显示当前目录名而不是完整路径。
# config.nu $env.PROMPT_COMMAND = { || "〉" } $env.PROMPT_INDICATOR = { || "〉 " }从长期实用角度看,Prompt 越短,你的有效屏幕面积越大。把时间留给真正提升效率的事,比如下面这套 coreutils 的 Windows 适配。
4. coreutils 的 Windows 生存实录
4.1 uutils/coreutils 与 GNU coreutils 的行为差异
从 GNU coreutils 迁移到 uutils 版本,大多数命令是透明的,但有几个差异值得知道。date的格式语法基本一致;readlink -f在 Windows 上表现弱一些,因为 Windows 的路径规范本来就和 Linux 不同;kill支持的信号机制也和 Unix 不太一样,Windows 下更像进程终止工具而非信号投递。
最实用的替代经验是:能用 Nushell 内部结构完成的操作,不要切回 coreutils。举例来说,按文件大小过滤,Nu 一行搞定;用外部find加管道再解析,反而绕远。
4.2 高频命令的实测示例
下面是我整理出的 Windows 开发里最常用的一组命令组合。假设你有一个项目目录,想要找所有*.log文件中包含 "ERROR" 的文件并按时间排序:
# 方式一:完全 coreutils 风格 grep -rl "ERROR" --include="*.log" . | xargs ls -lt # 方式二:Nushell 风格 ls **/*.log | where type == file | each { |f| if (open $f.name | str contains "ERROR") { $f } } | flatten | sort-by modified方式一适合临时快速操作,方式二适合在 Nushell 里继续做后续数据处理。两个交叉使用,几乎能覆盖所有日常场景。
sort、uniq、wc -l这些命令在 uutils 版本里性能也够用,处理几十万行日志没什么问题。之前我用 PowerShell 跑一个Get-Content xxx | Group-Object | Sort-Object count -Descending | Select-Object -First 20,写起来麻烦,性能也一般;换成sort | uniq -c | sort -rn | head -20之后,一下回到了熟悉的复杂度。
4.3 换行符、路径分隔符与编码三座大山
Windows 上跑 Unix 工具,绕不开这三个坑。
第一是换行符。很多从 Windows 写的文件是 CRLF,在grep正则里匹配末尾内容时会有意想不到的问题,比如$匹配不到期望的位置。我常用的处理方式是:
# 把 CRLF 转成 LF sed -i 's/\r$//' file.txt第二是路径分隔符。Windows 默认是反斜杠,很多 Unix 工具会把反斜杠当成转义字符,导致C:\Users\me路径被拆开。一般建议在脚本里统一用正斜杠,Nushell 和大多数新工具都接受C:/Users/me这种写法。coreutils 的 Windows 版本对正反斜杠的兼容比较好,但传参给其他工具时仍要小心。
第三是编码。Windows 老软件的默认编码经常是 ANSI 或 UTF-16,而 Nu 和 uutils 默认按 UTF-8 读。文件里出现中文乱码时,第一反应不是怪工具,而是先转码:
iconv -f UTF-16LE -t UTF-8 source.txt > target.txt这个命令在 uutils 里也能用,实测对 Windows 导出的 Unicode 文本非常有效。
4.4 与 PowerShell 留下的系统脚本共存
现实情况是,你的 Windows 机器上一定有历史遗留的 PowerShell / CMD 脚本,不能指望所有东西都迁到 Nushell 里。我采取的策略是:
- 日常交互命令走 Nushell + coreutils;
- 系统管理、注册表、服务操作仍然调用
powershell.exe -Command; - 写 Nushell 脚本时,用
^调用外部 exe,尽量减少心智负担。
比如查看 Windows 服务状态,我没有找一个 Nu 插件去复刻,而是直接:
^powershell.exe -NoProfile -Command "Get-Service | Where-Object { $_.Status -eq 'Running' } | Select-Object Name, DisplayName"Nushell 的输出会被当作外部命令标准输出来接收,配合lines和parse可以拿到结构化的表格。这套"外部命令 + Nu 解析"的组合,让我能用 Nushell 的语言逐步消化掉旧脚本留下的数据。
5. Fresh:把散落的配置文件收编成可复现状态
5.1 Fresh 的工作原理:极简点文件管家
Fresh 这个工具的核心机制并不复杂:它读取一个清单文件,默认位于~/.freshfiles,里面列着你想管理的 Git 仓库地址。执行fresh之后,它会把仓库克隆到本地缓存目录,再按规则把特定文件链接到你的用户目录或指定路径下。
这样的好处是,你的 Nushell 配置、Windows Terminal 的 settings 备份、甚至 coreutils 的环境变量脚本,都可以放在同一个 Git 仓库里。换新电脑时,装上 Ruby 和 Fresh,拉一下清单,执行一次fresh,整套环境就基本还原了。它比"手动拷贝配置文件"强在版本跟踪,改了配置可以回滚,犯错不至于把系统弄坏。
5.2 建立自己的配置文件仓库
我建议先单独建一个配置仓库,比如my-dotfiles,里面至少放这几个文件:
nushell/config.nu nushell/env.nu windows-terminal/settings.json common/.gitconfig然后在~/.freshfiles里写:
https://github.com/你的用户名/my-dotfiles.gitFresh 实际采用的链接规则有时会因版本变化有差异,所以我的建议是:第一次执行前先看fresh --help,确认链接语法;如果版本太老,就直接手动把仓库克隆到一个固定目录,再用一个小脚本一次性建立符号链接。经验是,不用在一开始就追求完美自动化,能把配置文件纳入 Git 管理,已经比默认状态前进了一大截。
5.3 Windows 上符号链接的特殊处理
Fresh 要把仓库里的文件链接到配置目录,本质依赖符号链接。在 Windows 上创建符号链接有两个条件:
- 需要“开发人员模式”,在系统设置里打开,让普通用户也能执行
mklink; - 或者管理员权限运行终端,但日常使用不方便。
如果不想开开发者模式,也可以改用“目录联接”(junction)。不过 Fresh 自己管理链接时会使用 Ruby 的文件操作,开发者模式没开的报错内容往往比较隐晦,可能只会提示Permission denied。我踩过这个坑,排查了很久才发现是链接权限问题。
开启开发者模式之后,建议验证一下:
New-Item -ItemType SymbolicLink -Path .\linktest -Target .\README.md能创建成功,Fresh 的路就畅通了。
5.4 日常维护节奏:不是一锤子买卖
dotfiles 管理最忌讳“配一次就再也不碰”。我的节奏是:
- 每次调整了
config.nu,顺手推送到配置仓库; - 每周至少在另外一台电脑上
fresh update一次,保证可复现性; - 遇到环境问题时,优先怀疑配置变更,用 Git 历史排查。
这个习惯让我在几次换电脑和重装系统的过程中几乎没有中断开发。比起重新搭建环境浪费的半天时间,前期花一点少量时间把配置纳入管理,回报非常高。
6. 整套方案跑了三个月后的几个结论
6.1 启动速度与资源占用实测
很多人担心 Nushell 比 PowerShell 慢,我实际对比下来:冷启动确实比 PowerShell 慢一点,大概多 0.3 到 0.5 秒,但如果你的config.nu里没有大量加载外部脚本,这个差距很难感知。Windows Terminal 的 Atlas 引擎渲染起来非常流畅,和旧的 conhost 相比提升巨大。
如果启动偏慢,优先清理config.nu里的use和source加载项,尤其是从网上下载的补全脚本,加载越多,启动越慢。我自己只保留了非常少的外置功能,其他都按需source。
6.2 中文与 UTF-8 的最终处理经验
在 Windows Terminal + Nushell + coreutils 这个组合下,中文支持整体是好的,但有几个细节需要固定下来:
- Windows Terminal 的 profile 里,默认代码页切到 UTF-8,或者至少保证单个 profile 的环境变量
PYTHONUTF8=1; - 文件内容如果是从老系统导出的,先用
iconv转码再进 Nu 处理; - 不要在 Nu 管道里混用 PowerShell 的
Out-File -Encoding utf8和 coreutils 的>重定向,两者默认换行和 BOM 处理不一致,容易生成脏文件。
只要统一为泳逸的 UTF-8,中文文件名、中文日志内容都能正常显示和检索。
6.3 这套方案适合谁,不适合谁
如果你主要工作是写代码、看日志、操作 Git、处理文本,这套组合会让你非常舒服,几乎可以把 Linux 上养成的命令行习惯平移到 Windows。
但如果你平时只是执行几条固定命令,对 shell 没有任何探索欲望,那用默认的 PowerShell 反而更稳。Nushell 的学习曲线还是有的,尤其当你试图把一些复杂的文本处理脚本改写成 Nu 风格时,前期会有一段不适应期。
6.4 最后分享一个小习惯:每季度重新评估配置
工具链是会进化的,Nushell 版本迭代很快,coreutils 的 uutils 项目也在持续完善。所以每过几个月,我会专门抽时间把配置里的别名、符号链接、路径声明整体检查一遍。这套环境真正的优势,其实不是某一个命令多好用,而是所有配置都在 Git 仓库里,你可以随时复盘自己当时的选择,也可以随时推翻重来。有了 Fresh 做兜底,改坏一个大不了回滚,这种安全感才是 Windows Terminal 开发体验里最值得投资的部分。