从 WSL 迁回原生 Windows:一个 Linux 用户要准备什么
本文完全由 WorkBuddy 独立收集资料、调试代码并完成文章写作。
我是 Alex Xiang,前百度/微博工程师,现在专注于 AI 工程与工具产品。更多文章欢迎关注微信公众号「字与码」。
上个月我把最后一批项目从 WSL 挪到了原生 Windows,前后折腾了两周,中间推倒重来过一次。
起因很朴素。WSL 那份内存占用开始让我难受,而且我发现在 WSL 里写代码、在 Windows 上验证结果这件事本身就是浪费:两端各维护一套工具链,文件在边界上来回搬,/mnt/c 和 \\wsl$ 各慢一次,出了问题还要先判断是 Linux 的问题还是跨界访问的问题。
但真正让我觉得这件事现在值得做,是今年 Windows 这边的几个变化。微软在 Build 2026 上放出了 Coreutils for Windows,把 GNU 那一套命令行工具用 Rust 重写后原生跑在 Windows 上;WinGet 的 Configuration 基于 DSC v3 终于能用,官方还开了一个 WindowsDeveloperConfig 仓,专门做“一条命令从裸机到可用”;再加上 Windows 11 自带的 sudo 和 Dev Drive,以前必须靠 WSL 补的那几块,原生侧基本补齐了。
需要提前说清楚:这不是一篇“Windows 比 Linux 好用”的文章。WSL 提供的是内核级一致,原生 Windows 提供的是工具级一致,两者差的正是文件系统语义、信号和权限模型。所以迁移的正确前提不是“Windows 变好了”,而是“我的工作负载是不是真的需要 POSIX 语义”。
先判断该不该迁
把话说直白一点,迁移划算的情形是这几种:
你的痛点主要来自跨文件系统边界。代码在 Linux 侧,IDE、浏览器、设计工具在 Windows 侧,每天在 /mnt/c 和 \\wsl$ 之间反复摩擦,构建慢、watch 丢事件、路径一长就出玄学问题。
你的目标平台本来就是 Windows。做桌面应用、Windows 服务、驱动、游戏、企业内网工具,代码在 Linux 里写、在 Windows 上验,中间那层转换纯属自找麻烦。
你嫌 WSL 的内存和磁盘开销。一份 Ubuntu 加上 vhdx 文件,动辄几个 GB 内存和几十 GB 磁盘,而且 vhdx 只会长不会缩。
反过来,下面这些情况我会建议留在 WSL:
需要 systemd、需要跑 Linux 容器、需要内核特性、需要 apt 装某个只有 Linux 构建的服务端组件;或者你要做的是服务端交付,希望本地和生产完全同构。这不是能力问题,是分工问题。最糟的结局是硬迁过去以后,在 Windows 上手工重建一套半残的 POSIX,花掉两周,最后得到一个更难维护的 Linux。

必须装的只有三个半
我最后稳定在新环境里的,只有这几个:
一、PowerShell 7。winget install —id Microsoft.PowerShell。注意这是替代 Windows 自带的 PowerShell 5.1,不是替代 bash。装完用 pwsh 启动,7.x 和 5.1 可以共存。
二、Coreutils for Windows。winget install Microsoft.Coreutils。这是本文最重要的一个包,下一节单独讲。
三、Git for Windows。你大概率已经有了。它的价值不只是 git,而是顺带把 Git Bash 装了进来——这是你唯一需要的“逃生舱”,后面会讲。
半个、PowerToys。winget install Microsoft.PowerToys。严格说它不是必需品,但 Command Palette(Win+Alt+Space)把“找文件、找应用、跑命令、装包”合成一个入口,Keyboard Manager 可以把按键映射改回你熟悉的位置。它是官方维护、自带更新、装完不用配置的那种工具。
另外两样不用装,因为 Windows 11 已经内置:sudo(24H2 起,设置里打开,或用 sudo config —enable normal),以及 Edit——一个类似 nano 的轻量命令行编辑器,在我这台机器上位于 C:\Windows\System32\edit.exe,所以你不需要为了改个配置文件去装 vim 或 nano。
Windows Terminal 也不用装,Windows 11 自带。它是这一切唯一需要记住的入口:PowerShell、Cmd、Git Bash、以后可能还用到的 WSL,全都是它的 profile,切换用下拉菜单就行。记住两个快捷键:Ctrl+Shift+P 打开命令面板,Alt+Shift+加号/减号 在当前窗口分屏。

Coreutils for Windows 是这次迁移里最值钱的包
这个包值得单独一节,因为它直接回答了你最担心的问题:我是不是得重新学一套命令?
答案是:大部分不用。
Coreutils for Windows 是微软维护的一套 UNIX 风格命令行工具,原生跑在 Windows 上。它不是模拟层,是基于开源项目 uutils/coreutils(GNU coreutils 的 Rust 跨平台重实现,也是不少现代 Linux 发行版里在用的那份)做的 Windows 构建版,MIT 许可。它把 coreutils、findutils(find、xargs)和一份 GNU 兼容的 grep 打包在一起发布。
实现方式挺聪明:只有一个 coreutils.exe,装在 C:\Program Files\coreutils</code> 下,然后为每个命令建一条 NTFS 硬链接——cat.exe、ls.exe、grep.exe 都指向同一个文件,磁盘上只有一份。你敲哪个名字,它就按哪个名字决定自己该干什么。所以它没有“装 100 个程序”的负担。
它提供什么:ls cp mv rm cat head tail wc sort uniq cut tee sleep uptime hostname date base64 expand 等等,加上 find、xargs 和 grep。
它不提供什么,这部分同样重要:chmod、chown、chroot、nohup、tty、who、kill、timeout。前六个是因为 Windows 根本没有对应概念——没有 uid/gid、没有信号、没有 tty;kill 和 timeout 也是卡在信号上。dir、more、paste、whoami 不给,是因为和 CMD 内置命令撞名。
还有一个坑必须提前知道,这也是我在自己机器上踩到的:PowerShell 的别名会抢先。
PowerShell 里 ls、cat、cp、mv、rm、pwd、echo 这些名字统统是别名,指向对应的 cmdlet。装了 coreutils 之后,你在 PowerShell 里敲 ls,拿到的仍然是 Get-ChildItem,不是 coreutils 的 ls。想要 coreutils 那版,得写 ls.exe。
这不是 bug,是设计。而且换个角度看它有好处:因为别名在,你的肌肉记忆还在。手指敲 ls 依旧列目录,敲 cat 依旧看文件,只是背后换了实现。真正需要 GNU 语义的时候(比如 grep -P、sort -V)补一个 .exe 就行。
顺便说一个我自己的发现:我机器上还留着历史遗物——一份 GnuWin32 装的 grep 和 awk,版本是 GNU grep 2.5.4,2010 年前的产物。这类东西一装就是十几年,跟 Coreutils 功能重叠,PATH 里谁先谁后全看运气。如果你也有一堆历史遗留的 Unix 工具,把它们清掉,只留一份。混着用比不用更难受。
PowerShell 要不要学
这是最核心的问题,我给你一个尽量诚实的答案。
你不需要“熟悉 PowerShell 的命令”,但你需要理解它的五个概念,并且知道去哪查。
先说为什么躲不掉。它是唯一永远在场的 shell——Windows 自带,任何一台机器都有。更要紧的是,凡是碰 Windows 自身的东西,PowerShell 都是最省事的那条路:服务、注册表、计划任务、ACL、UAC 提权、winget、Hyper-V、.NET、Azure/AWS 命令行工具,全都是 PowerShell 优先。你可以一直用 Git Bash 写业务代码,但一旦要动系统本身,绕开 PowerShell 的代价比学它高得多。
再说为什么没那么可怕。因为 PowerShell 的命名是可推导的,而 bash 不是。
bash 里 ls、cat、grep、awk、sed、tr、cut、wc、df、du——每个都是独立的历史遗留名字,彼此毫无规律,这才叫“要背”。PowerShell 用的是动词-名词结构:Get- Set- New- Remove- Test- Invoke- Start- Stop- 配对象名。你见过 Get-Content,就能猜出 Set-Content、Add-Content、Clear-Content 是干嘛的。掌握大约二十个 cmdlet 加这套构词法,覆盖面已经相当广。
第二个概念是对象管道。这是 PowerShell 真正比 bash 强的地方,不只是“另一种写法”:
# bash 里你得 awk 切列
Get-ChildItem C:\Windows\System32 -File |
Where-Object { $_.Length -gt 5MB } |
Select-Object -First 3 Name, @{n='MB'; e={[math]::Round($_.Length/1MB,1)}}
这里每一环拿到的都是真实的文件对象,不是文本行。$_.Length 是属性,不是第几列。5MB 也不用手算——PowerShell 认 KB/MB/GB/TB 后缀,实测 5MB 就是 5242880。
想知道一个对象有哪些属性能用,Get-Member(缩写 gm)是最有用的一条命令:管道接 | gm 就知道手上有什么牌。这比翻文档快得多。
其余三个概念一句话带过:参数绑定(参数名可以写前缀,唯一即可,所以 -Recurse 能写 -r)、模块自动加载、别名只是兼容层(ls 是别名这件事本身就说明它是给人手用的,不是给脚本用的——写脚本时应该用完整 cmdlet 名)。
最后给三个立刻见效的体验修正,尤其是你如果习惯了 Emacs/readline 的快捷键:
# 让 Ctrl+A / Ctrl+E / Ctrl+W / Ctrl+U / Ctrl+R 全部复活
Set-PSReadLineOption -EditMode Emacs
Set-PSReadLineOption -PredictionSource History -PredictionViewStyle ListView
Set-PSReadLineKeyHandler -Key Tab -Function MenuComplete
第一行把行编辑换成 Emacs 模式,Ctrl+A 到行首、Ctrl+E 到行尾、Ctrl+W 删一个词、Ctrl+U 删到行首——Linux 用户最舍不得的就是这几个键。第二行用历史记录做内联自动补全,第三行把 Tab 变成菜单式补全而不是循环切换。
这里有个实测的坑要提醒:-PredictionSource 只在真实交互控制台里生效。我把它放在脚本里跑的时候直接报错——The predictive suggestion feature cannot be enabled because the console output doesn’t support virtual terminal processing or it’s redirected。所以别在 CI 或重定向环境里加这一句,会失败。
bash 到 PowerShell 的实测对照表
下面这张表不是在文档里抄的,是我在自己这台机器上(PowerShell 7.6.6)逐条查 Get-Command 得到的结果,行为部分也都实际跑过。
你的习惯 在这台机器上实际是什么 怎么办 ls别名 → Get-ChildItem 手感保留,但输出是对象;没有 ls -l 这种参数 cat别名 → Get-Content 能用;cat -n 不存在 cp mv rm mkdir前三个是别名,mkdir 是函数 rm 的坑单独说,见下一节grep 关键词 文件无内置 用 Select-String;或 Coreutils、ripgrep find . -name '*.txt'命中的是 C:\Windows\system32\find.exe(DOS 版) 实测直接报 FIND: 参数格式不正确。用 Get-ChildItem -Filter *.txt -Recurse head tail wc uniq du都不存在 Get-Content -TotalCount、-Tail、Measure-Object -Line;或装 Coreutilstail -f app.log无 Get-Content app.log -Wait -Tail 20(实测两个参数都支持)which git无 (Get-Command git).Source,或 where.exe gitsed awk本机没有(我这份是 GnuWin32 遗留) 短文本用 -replace,复杂处理直接上 Python ps aux 加 grep 过滤别名 → Get-Process Get-Process 接 Where-Object Name -match ...kill -9 1234别名 → Stop-Process Stop-Process -Id 1234 -Forcetop df free都不存在 任务管理器、Get-PSDrive、Get-CimInstance Win32_OperatingSystem date不存在 Get-Date -Format 'yyyy-MM-dd HH:mm:ss'touch f不存在 New-Item f 或 Set-Content f -NoNewlinersync不存在 robocopy(系统自带),或 Scoop 装 rsynccron不存在 schtasks / 任务计划程序chmod +x不存在 见“四个文件系统真相” nohup x && 是 PowerShell 自己的运算符实测 cmd /c echo a&b 会启动一个后台 Job。要原样传参用 --%
最后一行值得展开,因为它是最容易让人懵的一种失败:PowerShell 会解析你传给原生命令行程序的参数。我用 cmd.exe /c echo a&b 测试,PowerShell 把 & 当成后台作业运算符吃掉了,直接起了一个名为 Job1 的 BackgroundJob;加上 —%(停止解析符)之后,后面的内容才原样交给 cmd。以后看到某个原生命令的参数莫名其妙被吃掉,先试 —%。
把 $PROFILE 当成 .bashrc
PowerShell 的启动脚本是 $PROFILE。路径值得单独记一下,因为 PowerShell 7 和 Windows 自带的 5.1 用的是两个不同目录:
PowerShell 7 : C:\Users\<你>\Documents\PowerShell\Microsoft.PowerShell_profile.ps1
PowerShell 5.1 : C:\Users\<你>\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
直接 echo $PROFILE 看当前 shell 用哪个,然后 code $PROFILE 或 notepad $PROFILE 打开,文件不存在就 New-Item $PROFILE -Force 建一个。
比 .bashrc 好的一点是:它是脚本,不是被 source 进来的配置文件。你可以正大光明地写条件判断和函数,不用担心语法怪。
我自己的 profile 里除了 Oh My Posh 的初始化,就是这几行:
# 中文环境:让 .NET 侧的输出走 UTF-8
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$OutputEncoding = [System.Text.Encoding]::UTF8
# 把手感接回来
Set-Alias open Start-Process
Set-Alias pbcopy Set-Clipboard
Set-Alias pbpaste Get-Clipboard
# 两个常用小函数
function ll { Get-ChildItem -Force @args }
function mkcd { param([string]$p)
New-Item -ItemType Directory -Force -Path $p | Out-Null
Set-Location $p }
open、pbcopy、pbpaste 这三个别名是我认为性价比最高的三条:它们零成本地把 macOS/Linux 的手感接回来,因为 Windows 那边刚好有等价物(Start-Process、Set-Clipboard、Get-Clipboard)。上面这些我都在无 profile 的子进程里实测过语法,全部可用。
中文用户先解决编码
这是中文用户特有的第一道坎,而且它在迁过去之前不会暴露——WSL 里一切正常,迁到原生就来。
我在自己机器上实测的现状是:PowerShell 7.6 里 [Console]::OutputEncoding 是 gb2312,chcp 报 936。也就是说控制台默认还在 GBK 世界里。
这两件事要分清楚:
[Console]::OutputEncoding 管的是 .NET 侧读写控制台时用哪个编码,profile 里设成 UTF-8 能解决 PowerShell 自己输出的问题。但控制台的代码页是另一回事——我设完 [Console]::OutputEncoding 之后,单独跑 chcp 依旧报 936。而新装的 coreutils 这类原生命令行程序,走的是控制台代码页。所以最稳的做法是两件一起做:profile 里设上面的两行,再想办法把控制台代码页也切到 65001(最直接就是在 profile 里跑一次 chcp 65001)。
顺带一个反向的坑:Set-Content 默认写 UTF-8 无 BOM,但有些 Windows 老程序(尤其是一些企业软件和早期的 CMD 工具)读配置文件时仍按 ANSI 解码。给 Windows 程序生的配置文件,出乱码就先试试 -Encoding utf8BOM 或 -Encoding oem。反过来,给 Linux 用的文件别加 BOM,否则 #!/bin/bash 开头多个字节,解释器会直接报错。
有些事只能用 PowerShell 做
迁移里最容易浪费时间的一种行为,是试图用 bash 去驱动 Windows。Git Bash 确实在,也确实能跑 bash 脚本,但它驱动不了 Windows 自己的抽象。
下面这些,老老实实用 PowerShell,别绕:
你要做的事 PowerShell 的做法 bash 侧为什么不行 提权执行 sudo <cmd>(Win11 24H2+ 内置)Git Bash 没有 UAC 概念 管服务 Get-Service / Restart-Service没有 systemctl,服务不是文件 定时任务 schtasks / Register-ScheduledTask没有 cron 也没有 crontab 改系统设置 New-ItemProperty HKLM:...没有 /etc,只有注册表 改文件权限 icacls / Get-Acl Set-Acl没有 chmod,是 ACL 查网络 Get-NetTCPConnection / Test-NetConnection没有 ss/netstat 输出格式 装软件 winget install没有 apt/yum
关于 sudo 补一句:它有三种模式,normal(内嵌在当前终端,最像 Linux,但权限边界最松)、forceNewWindow(默认,弹新窗口)、disableInput(当前窗口但禁止输入)。日常用 normal 最顺手,如果你机器上有别人用,建议留默认的 forceNewWindow——它的存在意义就是让你一眼看出“这条命令是提了权的”。切换用 sudo config —enable normal。
四个文件系统真相
从 Linux 过来,最需要重新建立直觉的就是文件系统。有四件事和 ext4 不一样,而且都会实实在在地咬人。
一、默认不区分大小写。Foo.txt 和 foo.txt 是同一个文件。这对从 Linux 拉下来的项目很致命——里面有 README.md 和 readme.md 时,clone 会覆盖或者报错。可以按目录打开大小写敏感:
fsutil file setCaseSensitiveInfo D:\projects\myrepo enable
fsutil file queryCaseSensitiveInfo D:\projects\myrepo
第二条是我实测过的输出形式(关闭状态下返回“已禁用目录 … 的区分大小写属性”)。但它有两个副作用要知道:有些程序假设文件系统不敏感,开了之后会行为异常;资源管理器的显示也会变得混乱。所以只对确实需要的那个仓库开,别对整个盘开。关掉时更要小心——同名不同大小写的文件可能被合并或隐藏,先备份。
二、行尾是 CRLF,而且会污染 shell 脚本。git 的 core.autocrlf 我建议设成 input,不要 true:true 会把工作区文件也转成 CRLF,一个 .sh 进去就变成 /bin/bash^M: bad interpreter。更可靠的做法是在仓库里放 .gitattributes,写死 *.sh text eol=lf,这样不依赖每个人本地的配置。
三、没有可执行位。chmod +x 在 Windows 上不存在,Git for Windows 默认 core.filemode=false。如果你写的脚本要在 Linux 的 CI 里跑,唯一跨平台可靠的做法是让 git 记住这个位:
git update-index --chmod=+x deploy.sh
四、文件名有禁区,而且文件被占用就动不了。保留名 CON PRN AUX NUL COM1-9 LPT1-9 不能用作文件名;文件名里不能出现 <、>、:、"、/、\、|、?、* 这九个字符;结尾不能是空格或点。
至于文件占用,我实测的错误信息是这样的:
The process cannot access the file '...' because it is being used by another process.
这条对 POSIX 用户来说最反直觉——Linux 上删掉一个正在被读写的文件是完全正常的事(inode 还在,进程继续用),Windows 上不行。所以“删不掉某个文件”往往不是权限问题,是某个进程开着它(常见嫌疑犯:编辑器、语言服务器、看图的窗口、杀毒软件)。查明谁占着可以用 handle.exe(Sysinternals),或者干脆用资源监视器的“关联的句柄”搜索。
还有一个连带陷阱:rm 删目录时的行为。实测下来,Remove-Item 对含有子项的目录如果不带 -Recurse,会弹一个确认提示,而这个提示连 -Confirm:$false 都压不住。我在非交互环境里跑的时候,它直接放弃了删除——脚本继续往下走,你以为删完了,实际目录原封不动地留在那儿。更糟的是在交互终端里它会停下来等你按键,CI 里就挂住了。
所以删目录的可靠写法是显式加两点:
Remove-Item $dir -Recurse -Force -Confirm:$false # 实测 328ms 静默完成
[System.IO.Directory]::Delete($dir, $true) # 走 .NET,绝不会弹提示
第二种在需要绝对静默的场景(脚本、CI)更省心,我在自己的机器上验证过它不产生任何交互。
用 Dev Drive 和排除项换回编译时间
Windows 上有一项性能损耗是 Linux 用户完全没概念的:Microsoft Defender 的实时扫描是同步的。每个编译产物、每个解包出来的依赖文件,写入时都要等杀毒引擎检查一遍文件头。装 node_modules 这种几万个小文件的活,光这一项就能拖掉一半时间。
有两条路。一是排除项:
Add-MpPreference -ExclusionPath "D:\projects"
需要管理员权限。这是安全取舍,别把整个盘排除,也别把软件下载目录排除。
二是 Dev Drive,这是微软为开发者专门做的卷:ReFS 文件系统 + Defender 的 Performance Mode(在受信任的 Dev Drive 上改成异步扫描)。ReFS 的关键能力是 写时复制的块克隆——pnpm/npm 从缓存往项目里复制依赖时,不再真的写一遍数据,只增加元数据引用,物理写入发生在你修改那个块的时候。
我看到的实测数据差异挺大:有人的对比是 npm install 从 58 秒降到 21 秒、clone 中型仓库从 42 秒降到 13 秒、解压 2GB 归档从 41 秒降到 19 秒;微软自己给的口径则是部分开发场景最多约 30%。差距这么大是因为收益完全取决于你的瓶颈在哪——如果是 I/O 密集(大量小文件创建、删除、遍历),收益明显;如果是 CPU 密集(编译大文件、跑类型检查),Dev Drive 帮不上忙。
建法:设置 → 系统 → 存储 → 高级存储设置 → 磁盘和卷 → 创建 Dev Drive,Windows 11 22H2 以上支持,至少留 50GB,实际用起来 100-250GB 更现实。
几个必须知道的坑:不能就地转换,只能新建卷再把文件搬过去;用 VHDX 形式建的 Dev Drive 常被备份工具跳过,要手动加进备份计划;还有报道说 ReFS 在高强度编译下会把 Windows 的 System 进程工作集撑大(需要在注册表里调 RefsDirtyPageThreshold 之类的参数),我是把它当“可能存在”的风险记着的,你如果用大项目,观察一下内存占用。
我的建议是把 Dev Drive 留给三类东西:活跃的代码仓库、包管理器缓存、构建输出目录。文档、照片、下载这些放普通 NTFS 就行,Dev Drive 不是拿来当杂物间的。

把整台机器写成一个文件
Windows 这边对应 Homebrew 的是 WinGet,而它比 brew bundle 更进一步的地方在 Configuration:用一个 YAML 文件(基于 DSC v3)同时描述“装哪些包”和“改哪些系统设置”,然后一条命令应用。
winget configure --enable # 首次需要启用
winget configure -f .\dev-config.winget `
--accept-configuration-agreements --disable-interactivity
两个实测的注意事项:非管理员环境下用 winget configure 之前,必须先装 VC++ 运行库(winget install Microsoft.VCRedist.2015+.x64),否则会以一个含混的 internal error 失败;另外这个命令本身在我这台机器上的 winget(v1.29.290)里是可用的,能用 winget configure —help 确认。
真正值得看的是微软今年在 Build 2026 上开源的官方配置仓 microsoft/WindowsDeveloperConfig,里面有三种配置:Windows Dev Config(整台开发工作站,一条命令,会重启一次)、WSL Comfort(WSL 侧的 shell 环境)、Workloads(单语言工具链,Node/Python/Rust/Go/Java/.NET/SQL/PHP 各一份)。仓库里的配置都做了幂等和 CI 测试,反复跑是安全的。
Windows Dev Config 装的东西挺有代表性:Windows Terminal、PowerShell 7、Git、GitHub CLI、Copilot CLI、VS Code、.NET SDK 10、Python 3.14 + uv、Node LTS + nvm、Coreutils for Windows、Windows App CLI、Oh My Posh、PowerToys;然后把 PowerShell 7 设成默认 profile、字体换成 Cascadia Mono NF、开深色主题、开开发者模式、开 sudo、开长路径、装 WSL + Ubuntu。
但我更想让你注意它关掉了什么:文件扩展名显示打开、隐藏文件显示、资源管理器打开到“此电脑”、关闭最近文件与云推荐、任务栏去掉小组件按钮、开始菜单去掉推荐、关掉 Bing 搜索建议、Edge 关掉首启向导。
这份清单的信息量比装机清单更大——它等于微软自己承认了哪些默认设置在妨碍工作。我把这个仓当“设置清单”读了一遍,挑了自己要的部分,没有整仓套用。理由很实在:Dev Home 才停掉没多久,这是微软第二次尝试干同一件事,谨慎一点不亏。
我没装的软件,以及为什么
你提的“尽可能少引入新软件”是这个决策里最重要的约束,所以我把“没装什么”也写清楚。
Cygwin——不装。它活着主要是因为历史。要命令行工具,Coreutils 加 Git Bash 够了;要 POSIX 编译工具链,MSYS2 更现代。Cygwin 那套 DLL 映射的环境一致性最差,启动开销和 PATH 污染倒是很实在。
MSYS2——日常不装。只在需要本地编译带 autotools 的 POSIX 依赖时才有意义。真需要的时候再装也不迟。
Chocolatey——不装。它是为“车队管理 + 私有包仓 + 审计”设计的,官方还有付费的 dashboard。单机开发者用 winget 加(可选的)Scoop 就覆盖了。
Nushell——不装。它很好,但它是第三套语法。你正在从 bash 迁到 PowerShell,再叠一套只会是净负担。
一堆 Rust 版 CLI 替代品——选择性装。eza、bat、fd、zoxide、ripgrep 都很优秀,但每装一个,你就多一份“它在 Windows 上的行为差异”要记。我这里只留了两个:ripgrep(大仓库里 grep 的速度差是真的,而且是数量级)和 zoxide(跨盘跳目录,省掉大量 cd)。
这里有一条我自己总结的判据,比“这工具好不好”管用得多:
装一个工具,如果它替代的是你已经会的东西,成本是学一套新参数。如果它替代的是你本来要新学的 PowerShell 命令,成本是省掉的。后者才值得装。
按这个判据,ripgrep 比 eza 更值得——因为“在目录里递归搜文本”是你每天都在做的事,而 Select-String 的参数你还没形成肌肉记忆,rg 则是你已经会的那个语法。
还有一条关于逃生舱的建议:Git Bash 别卸。它是“少装新软件”这件事上最漂亮的一笔——你没为它多装任何东西,它跟着 Git 就来了。别人的 install.sh、CI 里跑过的 bash 脚本、一次性处理文本的小脚本,继续用它写就行。迁移的目标不是“不再使用 bash”,而是“不再依赖 WSL 才能用 bash”。
迁移清单
最后给一份可执行的清单,按顺序做:
步骤 命令 / 位置 为什么 1 winget install --id Microsoft.PowerShell要有 pwsh,才能谈后面 2 winget install Microsoft.Coreutils省掉学一百条新命令 3 winget install Microsoft.PowerToysCommand Palette 换掉 Spotlight 4 设置里开 sudo,或 sudo config --enable normal 免掉每次开管理员终端 5 开长路径(LongPathsEnabled 注册表或组策略) .NET 程序自己能处理,老工具不行 6 建 Dev Drive,把活跃仓库搬进去 小文件场景收益最确定 7 Add-MpPreference -ExclusionPath 加项目目录别让杀毒拖编译 8 编辑 $PROFILE:编码、Emacs 模式、别名、函数 这是你的 .bashrc 9 git config --global core.autocrlf input + 仓库放 .gitattributes保护 shell 脚本 10 git update-index --chmod=+x *.sh可执行位只能靠 git 记 11 清理历史遗留的 GnuWin32 / 重复 Unix 工具 混着用比不用更糟 12 winget export 存档,以后可以一键复原换机器不用重来
顺手补一句关于长路径的实测:我机器上 LongPathsEnabled 是 0,但 PowerShell 7 依然成功创建了长度 367 的嵌套目录——因为 .NET 自己是长路径感知的。所以这个开关真正影响的是非 .NET 的老工具和部分安装程序,别以为 PowerShell 能建就万事大吉。
最后说回那个最核心的问题。
PowerShell 不熟悉,不影响你把项目跑起来。ls、cat、rm 这些别名让你第一天就能干活;Coreutils 补上 GNU 那边缺的;Git Bash 兜住 shell 脚本。真正会让你付出代价的,是试图用 bash 的思维去管理 Windows——去 /etc 找配置、想用 systemctl 管服务、指望 cron 跑定时任务。
所以我的建议是:把 PowerShell 当成一门“陌生但规律性很强”的工具,用两周时间每天学一两个 cmdlet,遇到问题先 Get-Help 命令 -Examples,想知道手上是什么对象就 | gm。不用背,但要知道去哪查——这两件事的差距,比“会不会 PowerShell”大得多。
微信公众号
欢迎关注「字与码」
如果这篇文章对你有用,也欢迎在微信里继续关注后续更新。
X / Twitter
关注 @ax2_zicode
更即时的技术观察、新文章提醒和一些短想法会发在 X 上。