路径与 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 就多一格绿灯。