说实话,Windows 的命令行这几年进步是肉眼可见的,但离“舒服”还是有段距离。默认的 cmd 还是老样子,PowerShell 语法我用不惯,装了 WSL 又觉得绕。直到我把 Fresh、Nushell、coreutils 这三样东西凑成一套,再配 Windows Terminal 当外壳,才真正觉得在 Windows 里敲命令是种享受。
这篇文章要聊的就是这个组合。Fresh 解决“软件怎么装”,Nushell 解决“命令怎么敲”,coreutils 解决“常用 Unix 命令哪里有”。适合谁看?如果你平时在 Windows 上做开发,想拥有一个干净、现代、命令齐全的终端环境,又不想整天跟 PowerShell 的语法搏斗,这篇内容就是冲你来的。我尽量把每一步都写清楚,包括踩过的坑。
1. 这个组合到底在解决什么问题
1.1 Windows 终端生态的三大痛点
先说第一个痛点:默认 shell 战斗力太弱。cmd 诞生这么多年,交互体验和脚本能力基本停留在远古时代,管道只能处理文本,循环、函数、错误处理都很原始,写超过十行的批处理脚本简直是折磨。PowerShell 能力确实强,但语法密度高,$符号满天飞,对象管道虽然厉害,可每次都要记| Select-Object、| Where-Object这一串,换台机器还会遇到执行策略、版本兼容各种问题。很多人干脆装个 Git Bash 凑合,但 Git Bash 的环境跟 Windows 本机是隔离的,装 Python、Node、Docker 之类的工具链时,两边 PATH 经常打架。
第二个痛点是包管理缺失。Windows 没有 apt、brew 那种统一好用的包管理器,winget 起步晚、覆盖面有限,Chocolatey 动不动要管理员权限,Scoop 更偏绿色软件但有自己的目录规则。这导致你装个软件经常要去官网找安装包,一路 Next 点完,再手动配 PATH,装多了之后系统环境变量变得一团乱。
第三个痛点是 Unix 命令缺失。开发中你会频繁用到 grep、ls、cat、rm、find、sort、uniq、head、tail 这些命令,Windows 原生一个完整实现都没有。PowerShell 里有 Get-Content、Select-String 这类替代品,名字长、参数习惯完全不同。Git Bash 里倒是齐全,但环境隔离问题又来了。总之,Windows 默认的命令行体验就是:能干活,但干得憋屈。
1.2 Fresh、Nushell、coreutils 各自扮演什么角色
这三样东西的分工很明确。
Fresh 是一个面向 Windows 的命令行优先包管理器,你可以把它理解成“给终端准备的应用市场”。它的特点是轻量,不常驻后台,不用图形界面,下载、安装、更新都在终端里完成。用它装软件,比去官网手动下载、配 PATH 要省心得多。
Nushell 是日常输入的 shell,用 Rust 写的。它最核心的特点就是管道里传输的不是文本而是结构化数据,命令输出默认就是表格。你在别的地方用awk折腾半天的活儿,在 Nushell 里可能就是一行where加一个select的事。
coreutils 是 GNU coreutils 的一个 Rust 重写版本,项目名是 uutils/coreutils。它把 ls、cat、grep、rm、cp、mv、sort、uniq、head、tail 这一百多个常用 Unix 命令搬到了 Windows 上,而且不是模拟器,是原生可执行文件。
三者的关系,我用一个生活化的类比:Fresh 是采购员,负责把该装的工具都装到位;Nushell 是工作台,你在这个台子上干活;coreutils 是挂在墙上的工具箱,缺什么工具伸手就能拿。三者各管一段,互相不抢活。
1.3 为什么把这三样凑成一套
这三样东西放在一起其实挺偶然,但它们有几个共性让我觉得特别搭。
第一,都偏现代、轻量、以终端为第一优先级。Fresh 不需要你开一个图形界面管理软件,Nushell 没有乱七八糟的 GUI 配置窗口,coreutils 就是一堆 exe。它们都不需要额外常驻服务,不占系统资源。
第二,都活跃在 GitHub 上,更新勤快,社区氛围好。用 Rust 写的 Nushell 和 uutils/coreutils 本身就同属一个生态,互操作起来很自然。Fresh 把软件装进预设目录、配好 PATH,Nushell 启动后自动就能发现这些命令,coreutils 补齐的命令又正好解决了文本处理的缺口。
第三,这三样不是强绑定的。你完全可以用 Scoop 替代 Fresh,用 PowerShell 替代 Nushell,用 wsl 里的 coreutils 替代本机版。但我实践下来,这三样配合在一起最省心,因为它们都不需要管理员权限(装到用户目录就行),也不会往系统里塞一堆驻留进程。
2. 环境搭建:从零装出这套终端组合
2.1 先装 Windows Terminal,别让命令在旧窗口里裸奔
很多教程一上来就让你装 shell,但我觉得第一步应该是先装 Windows Terminal。为什么?因为 Windows 默认的 conhost 老终端没有 GPU 渲染,窗口拖起来都卡,多标签几乎不可用,字体和配色支持也很差。Windows Terminal 相当于一个容器,它本身不负责执行命令,而是负责把 shell 呈现得好看、好用。
安装方式很简单,用 winget 一条命令:
winget install Microsoft.WindowsTerminal装好后在开始菜单找 Windows Terminal,或者直接在运行里输wt打开。装好之后你会立刻感受到差距:支持多标签、支持分屏、支持自定义字体和主题、支持快捷键。这套组合的所有体验都建立在 Windows Terminal 上,所以先把它装好。
为什么不直接用老终端?因为 Nushell 默认输出的表格、高亮、颜色在老终端里会显示得很糟糕,很多 ANSI 颜色和 Unicode 字符在老终端里要么乱码要么直接消失。Windows Terminal 配合 Nushell,才算完整发挥这套组合的实力。
2.2 用 Fresh 做包管理入口
Fresh 的安装不像 winget 那样一条命令搞定,需要手动下载解压,但步骤不难。
先建一个统一工具目录,我习惯用C:\tools。然后去 Fresh 的 GitHub Releases 页面下载最新 Windows 压缩包,解压后把fresh.exe放进C:\tools\fresh这个目录。接着把C:\tools\fresh追加到系统 PATH 里,这一步可以在“设置 → 系统 → 关于 → 高级系统设置 → 环境变量”里操作。最后重开终端,输入fresh --help验证。
这里有个细节值得说一下:为什么要统一放到C:\tools而不是随意找个目录?因为后面 Nushell、coreutils 都会装到这里,这样C:\tools就成了你的命令行工具大本营,备份、迁移、清理都很方便。PATH 里只需要多配一个C:\tools\fresh,其他的工具都按自己目录放好,再统一追加路径。
如果你觉得 Fresh 用不惯,或者在某些网络环境下下载实在困难,可以直接用 winget 或者 Scoop 替代。后面的安装步骤我会把 Fresh 的操作换成对应的 winget 命令,效果一样。
2.3 安装 Nushell 并设为默认 shell
Nushell 的安装方式有好几种,按优先级排列:
# 用 Fresh(如果源里能找到) fresh install nushell # 用 winget winget install Nushell.Nushell # 用 Scoop scoop install nushell如果不想依赖包管理器,也可以直接去 Nushell 的 GitHub Releases 页面下载 zip 包,解压后把nu.exe放到C:\tools\nu,再把C:\tools\nu加进 PATH。
装好后打开新终端,输入nu进入 Nushell,看到类似~>的提示符就算成功了。接下来做两件事。
第一件事,在 Windows Terminal 里新增一个 Nushell 配置项。打开 Windows Terminal 设置,左侧选“配置文件”,点“添加新配置文件”,命令行填C:\tools\nu\nu.exe,名称填 Nushell。如果你喜欢改 JSON,也可以直接编辑 settings.json,在profiles.list里加一段:
{ "name": "Nushell", "commandline": "C:\\tools\\nu\\nu.exe", "guid": "{2a4e1f9c-9b6c-4f0d-a9e2-6e8a4c6f3e99}" }guid 可以随便填一个唯一值,Windows Terminal 没有强制要求,但最好别跟已有的重复。
第二件事,把 Nushell 设为默认 shell。还是在设置里,找到“启动”页,把“默认终端配置文件”改成上面新建的 Nushell。这样打开 Windows Terminal 就直接进入 Nushell,不用每次手动输入nu。
第一次启动 Nushell 时,它会在%APPDATA%\nushell目录下自动生成两个配置文件:env.nu和config.nu。前者管环境变量和启动逻辑,后者管 shell 外观和行为。后面要改 PATH、改主题、加别名,都是编辑这两个文件。
2.4 安装 coreutils:补齐 Unix 命令
coreutils 这里我强烈推荐 uutils/coreutils,而不是老的 GnuWin32 或其他分支。原因很简单:GNU 官方 coreutils 在 Windows 上编译困难,古老的 GnuWin32 已经很多年没更新了,uutils 是 Rust 重写版,原生支持 Windows,性能和兼容性都好得多。
安装步骤:
- 去 uutils/coreutils 的 GitHub Releases 页面下载 Windows 版本压缩包,文件名类似
coreutils-0.0.27-x86_64-pc-windows-msvc.zip。 - 解压到
C:\tools\coreutils,里面是一堆 exe,比如ls.exe、cat.exe、grep.exe、rm.exe等等,数量超过一百个。 - 把
C:\tools\coreutils加进 PATH。 - 重开终端,输入
ls --version验证。
装完之后,你会发现C:\tools下已经整整齐齐躺着 fresh、nu、coreutils 三个目录。再验证几个常用命令,比如grep --version、cat --version,能正常输出版本信息就说明 coreutils 生效了。
这里要提前打个预防针:Nushell 自己也内置了ls、cp、mv、rm这些命令,它们会优先执行,而不是 coreutils 版本。这不是 bug,反而是设计。想强制调用 coreutils 版本时,用^ls、^rm这种带^前缀的写法就行。关于这个冲突怎么处理,后面第三章会详细说。
3. Nushell 与 coreutils 的搭配实操
3.1 Nushell 的结构化数据思想
Nushell 最核心的设计思想是“结构化数据管道”。传统 shell 的管道传的是纯文本,命令输出是一行行字符串,你想取其中某列就得用 awk 这类工具去切。Nushell 的管道传的是对象,每行数据自带字段名,命令和命令之间交换的是“表格”而不是“文本”。
我举一个最直观的例子:列出当前目录下所有大于 1MB 的文件,按大小从大到小排,只看文件名和大小。
Nushell 里就这么写:
ls | where size > 1mb | sort-by size --reverse | select name size输出就是一张干净的表格。如果换成传统方式,大概是:
ls -la | awk '$5 > 1048576 {print $9, $5}' | sort -k2 -rn这段在 Linux 上用没问题,但在 Windows 上,awk有没有都是个问题。这就是 Nushell 的优势:数据天然结构化,过滤、排序、取列都不需要外挂工具。
再比如统计当前目录下所有.log文件的总行数:
ls **/*.log | each { |f| open $f.name | lines | length } | math sum这一条链完成的事情,用 cmd 写批处理可能要十几行。Nushell 的语法也更接近日常语言,where、select、sort-by读起来就是英文语义,不用记那些缩写参数。
3.2 让 coreutils 在 Nushell 里正常工作
Nushell 虽然内置了很多命令,但一百多个 Unix 工具不可能全部内置进去。文本处理、权限管理、文件比较这些场景,还是需要 coreutils 来填坑。
Nushell 的命令查找逻辑是:先查内部命令,再查外部程序。所以当你输入ls,执行的是 Nushell 内置的ls;输入^ls,执行的才是 PATH 里的 coreutils 版本。同理,grep会调用 Nushell 自己的 filter 子命令,而^grep会调用 coreutils 的 grep。
我的使用习惯是:能用 Nushell 内置命令的场景优先用内置命令,因为内置命令的输出自带类型信息,管道里能直接访问字段;只有在需要 Unix 风格参数、或者内置命令没有对应功能时,才用^前缀调用 coreutils。
比如 Nushell 没有内置head、tail的完整实现,这时候就可以直接用 coreutils:
^head -20 access.log ^tail -f error.log还有文件权限相关操作,Windows 原生没有chmod这个概念,但 coreutils 也提供了一部分文件操作,在跨平台脚本里偶尔能派上用场。
在 Nushell 的配置文件里,也可以给常用外部命令设置别名。比如:
alias grep = ^grep alias cat = ^cat这样你日常敲grep、cat时,走的是 coreutils 版本。但要注意,ls、rm、cp、mv这几个自带命令我还是建议保留 Nushell 版本,因为它们的输出更结构化,删除时的回收站保护也更安全。
3.3 一个把数据管起来的具体示例
光说理论不够,我演示一个比较实用的场景:扫描当前目录下所有文件,找出大小超过 10MB 的非临时文件,按大小排序取前 10 个。
Nushell 写法:
ls **/* | where type == file | where size > 10mb | where name !~ '\.tmp$' | sort-by size --reverse | first 10 | select name size这条管道完成的工作,在传统 shell 里需要find、grep、sort、head、awk好几种命令组合,而且 Windows 原生一个都没有。Nushell 里用ls **/*递归列出所有文件,where连续过滤,sort-by排序,first取前几条,每一段都清晰可读。
再演示一个 Nushell 和 coreutils 混用的例子:用 coreutils 的ls -la输出文本,然后交给 Nushell 重新结构化。
^ls -la | lines | parse '{perm} {links} {user} {group} {size} {month} {day} {time} {name}'这条命令先用 coreutils 的ls输出 Unix 风格的长列表,再用 Nushell 的lines把多行文本拆成列表,最后用parse按模板拆出列。实际输出时,某些列的间距可能跟模板不完全一致,你需要根据实际输出微调模板字段。
这就是组合的威力:coreutils 负责生成文本,Nushell 负责把文本重新结构化成表格,两边互补,而不是互相替代。
3.4 配置提示符、主题与别名
Nushell 的体验有一半靠配置。第一次启动生成的两个配置文件里,config.nu管外观和行为,env.nu管环境变量。
先关掉每次启动时的 banner 广告:
$env.config.show_banner = false再改主题。Nushell 支持自定义主题配色,在config.nu里修改$env.config.color_config即可。官方文档里有很多现成主题可以抄,我自己用的是 Nord 配色,在终端里看起来比较清爽。代码大致长这样:
$env.config.color_config = { separator: "#88C0D0" leading_trailing_space_bg: "#3B4252" header: "#81A1C1" empty: "#B48EAD" bool: "#5E81AC" int: "#D08770" filesize: "#A3BE8C" duration: "#88C0D0" date: "#88C0D0" range: "#88C0D0" float: "#D08770" string: "#A3BE8C" nothing: "#D08770" ... }提示符如果想做得更花哨,可以用 Starship。安装 Starship 后,在 Nushell 里执行:
starship init nu | save -f ~/.cache/starship/init.nu然后在config.nu末尾加一行:
source ~/.cache/starship/init.nu就可以在提示符里显示 Git 分支、Python 虚拟环境、上一条命令执行耗时这些信息了。
别名方面,Nushell 支持两种方式。一种是简单别名:
alias ll = ls -l另一种是定义带参数的函数。比如把grep强制指向 coreutils 的 grep,可以这样:
def rgrep [...args] { ^grep ...$args }配置文件修改后不会自动重载,需要重启 Nushell 或者用source config.nu手动加载一次。
4. 常见问题与排查技巧实录
4.1 命令找不到或 PATH 不生效
这套组合最常遇到的问题就是刚装完某个工具,新开终端却提示命令找不到。
通常原因有两个:一个是 PATH 写入系统环境变量后,Windows Terminal 不会立刻刷新环境变量,旧窗口里执行不到新路径。解决办法很简单,关掉所有终端窗口重新打开,再不行就注销重新登录。
另一个原因是 Nushell 的 PATH 机制跟传统 shell 不太一样。Nushell 读取的是$env.PATH,这个变量在env.nu里可以自定义。如果你在系统环境变量里加了C:\tools\nu,但 Nushell 启动时它自己的 env.nu 又把 PATH 覆盖了,那就有可能找不到。解决办法是打开%APPDATA%\nushell\env.nu,在里面显式追加:
$env.PATH = ($env.PATH | prepend "C:\\tools\\nu") $env.PATH = ($env.PATH | prepend "C:\\tools\\coreutils")改完重启 Nushell,用$env.PATH检查是否包含这些目录。
4.2 coreutils 命令和 Windows 原生命令冲突
Nushell 内置命令和 coreutils 同名,这个很多人第一次遇到会懵。比如输入rm -rf temp,可能执行的不是想象中 coreutils 的rm,而是 Nushell 内置的删除命令,参数解析方式完全不同。
解决方式前面提过:在命令前加^强制调用外部程序。但更推荐的做法是“按场景选工具”:
- 删除、复制、移动文件:用 Nushell 内置的
rm、cp、mv,因为它们有回收站保护和类型化输出,对 Windows 用户更友好。 - 列出文件:用 Nushell 内置的
ls,输出是结构化表格。 - 做文本处理、管道给其他脚本:用 coreutils 的
cat、grep、head、tail。
如果你确实想让某个命令始终走 coreutils 版本,可以在config.nu里用别名覆盖,但我不建议对rm这么做,因为 Nushell 内置的rm会做删除确认,能防手滑。操作失误的代价远比你省掉一个^符号的成本高。
4.3 配置文件写挂之后怎么救
Nushell 的配置改坏了,启动时会报错,严重的时候连交互界面都进不去。我第一次改主题时手滑少打了一个括号,结果 Nushell 直接罢工。
这时候最快的救法是用无配置模式启动:
nu --no-config进入一个最干净的 Nushell 环境,然后用记事本打开%APPDATA%\nushell\config.nu,把出错的那段注释掉或修复,再重新启动 Nushell。
养个好习惯:每次改配置前先复制一份备份。
copy config.nu config.nu.bak改挂了直接copy config.nu.bak config.nu恢复,比临时修要快得多。
4.4 Fresh 下载慢或找不到包怎么办
Fresh 本身依赖网络下载软件包,如果你在 GitHub 下载资源时网络很慢,或者 Fresh 的软件源里根本没有你想要的那款工具,别硬等,直接绕开它。
绕开方案很简单:去对应工具的 GitHub Releases 页面手动下载压缩包,解压到C:\tools下面对应目录,手动把目录加进 PATH。最终效果跟 Fresh 安装一模一样,只是多花一分钟动手。
Nushell 和 coreutils 的官方 Releases 页面都有详细的安装说明,按步骤来不会出错。等后续 Fresh 源里补上了这些包,你随时可以用fresh install nushell重新接管,不影响已有配置。
包管理器这类工具,没必要执着于一家。我用 Fresh 是因为它轻量、统一,但它不是这套组合里不可替代的部分。能让你开发效率提升的是 Nushell 的结构化管线和 coreutils 补齐的命令,包管理器只是搬运工。
最后再分享点个人体会。我这套组合用的时间越长,越发现真正带来效率提升的不是某个工具多强,而是它们凑一起之后,Windows 命令行那种“缺这缺那”的别扭感消失了。刚开始换到 Nushell 时,我也花了不少时间折腾主题和别名,后来才慢慢意识到,最值钱的是管道里的结构化数据——以前写脚本要反复处理文本格式,现在直接用字段名取数,清爽太多。如果你想迁移,别急着一次全换,先装 Windows Terminal,再把 Nushell 装上跑一两天,看看习惯不习惯,最后把 coreutils 补上。至于 Fresh,等你的工具多到需要统一管理时,你自然会想用它。