路径与 MSYS:最大的行为差异
对「bash on Windows」的每个前任,路径都是一道必答题:要么像 Git Bash 一样在
spawn 前启发式改写参数,要么像 WSL 一样要求你换一套 /mnt/c 写法。
niubash 的答案是第三种:四大生态的路径方言全部接受且真实折叠,而
argv 本身逐字节原样——因为路径翻译是表驱动的引擎职责,不是藏在子进程
里的模拟层。
四大方言,一个引擎全收
| 方言 | 写法示例 | 折叠行为 |
|---|---|---|
| Windows 原生 | C:\repo、C:/repo | 第一公民——原生形态就是 shell 的内部表示,不存在「转换成」的问题。 |
| MSYS / Git Bash 形 | /d/repo、/tmp、/usr/bin | native 子进程收到 D:\repo;/usr/bin、/etc、/tmp 映射到安装根下的真实 Windows 目录。例外:cmd.exe 收到 /d/repo 原样(/d 与其自身开关无法区分)。 |
| WSL 形 | /mnt/d/repo | 真实折叠到 D:\repo(pwd -P 报 /d/repo)。对照:Git Bash 的 /mnt/d 是假阳性——静默落到安装根 D:\Git\mnt\d。 |
| Cygwin 形 | /cygdrive/d/repo | 外部命令层全认;内建面的分支补齐中(rubash#850)。 |
翻译规则是表驱动且与上下文无关的(rubash#600):不猜「这个参数 像不像路径」、不看参数位置、不看目标存在与否——即将创建的路径也得到合法的 Win32 形态。随包的 PATH-MATRIX 以约 100 条探针逐格实跑了方言 × 执行层的全量 矩阵,缺口全部开票在案(WinuxCmd#1244、rubash#850、niu-git#18)。
argv 逐字节原样:MSYS_NO_PATHCONV 不存在
Git Bash 的 MSYS 运行时会在 spawn 前启发式改写看起来像路径的参数:
/c 变 C:\,不存在的 /foo 被展开成 Git
安装路径,git grep "/fn main" 的模式被静默改写。所以每个老用户都被迫
学会 MSYS_NO_PATHCONV / MSYS2_ARG_CONV_EXCL 这两个咒语——
关闭开关的存在本身就是官方对问题的承认。
niubash 没有 MSYS 运行时,这两个环境变量在这里不存在,也不需要存在。
界线是成文的核心不变量「该 verbatim 的不转换」:commit 消息、分支名、引号内的
操作数,以及 grep/sed/awk/find 的语法数据(模式、脚本文本、-name
类值)、/cygdrive/…、/dev/*、环境变量通道、所有非路径
形态的词——逐字节抵达子进程。唯一的开关
__RUBASH_ARGV_DIALECT=legacy 是文档化映射的方言选择器(应急回退到
旧行为),不是「可能猜错的启发式」的救生闸。
$PWD 恒为 POSIX 形态(如 /d/repo)是配套裁决:这是
nvm 死循环根因(rubash#537)修复的同一决定,生态脚本对 $PWD 的
POSIX 假设因此成立。
uname 报 MSYS_NT:有意的兼容人设
在 Windows 上,uname -s 报 MSYS_NT-…
(设置了 MSYSTEM 时按 MSYS2 的方式映射为
MINGW64_NT- 等),$OSTYPE=msys、
$MACHTYPE=x86_64-pc-msys。这不是在伪装一个运行时,而是
兼容 persona(rubash#154):大量生态脚本以
case "$(uname -s)" 匹配 MINGW*|MSYS*|CYGWIN*
作为分支门,人设让这些门打开、脚本走它们被测得最多的路径。标签之下
没有任何 MSYS2 运行时:没有 msys-2.0.dll、
没有 cygwin1.dll、子进程内没有任何 POSIX argv 转换层。
一行对照:Git Bash = MSYS2 运行时 + 真 bash;niubash = 原生引擎 +
兼容人设。人设随处披露(--help/--identity),
且 RUBASH_IDENTITY=native 可切换为诚实原生面
(uname -s → Windows_NT、OSTYPE=windows)。
诚实段:写侧缺口收口中
边界契约不等于零缺口。写侧命令的裸盘符根缺口正在收口:
#1230 审计了 15 个写侧命令,其中 mkdir 的真写格(#1233)
最急。边界契约的价值正在于此——缺口可枚举、可开票、可修,而不是散落在
「有时像 Linux 有时不像」的雾里。每收一格,PATH-MATRIX 就多一格绿灯。