Paths & MSYS: the biggest behavioral difference

For every predecessor of "bash on Windows", paths are the compulsory question: either rewrite arguments heuristically before spawn like Git Bash, or force a new /mnt/c dialect like WSL. niubash's answer is a third one: all four ecosystem path dialects are accepted and genuinely folded, while argv itself arrives byte-for-byte verbatim — because path translation is a table-driven engine responsibility, not a compatibility layer hiding inside child processes.

Four dialects, one engine accepts them all

DialectExampleFolding behavior
Windows native C:\repo, C:/repo First-class citizen — the native form is the shell's internal representation; there is nothing to "convert to".
MSYS / Git Bash form /d/repo, /tmp, /usr/bin Native children receive D:\repo; /usr/bin, /etc, /tmp map to real Windows directories under the install root. Exception: cmd.exe receives /d/repo verbatim (/d is indistinguishable from its own switches).
WSL form /mnt/d/repo Genuinely folded to D:\repo (pwd -P reports /d/repo). Contrast: Git Bash's /mnt/d is a false positive — it silently lands in the install root D:\Git\mnt\d.
Cygwin form /cygdrive/d/repo Fully accepted at the external-command layer; the builtin-side branch is being completed (rubash#850).

The translation rules are table-driven and context-free (rubash#600): no guessing "does this argument look like a path", no looking at argument position, no checking whether the target exists — paths about to be created also get a legal Win32 form. The shipped PATH-MATRIX runs ~100 probes across the full dialect × execution-layer matrix, and every gap has a filed ticket (WinuxCmd#1244, rubash#850, niu-git#18).

中文原文 · Chinese original

翻译规则是表驱动且与上下文无关的(rubash#600):不猜「这个参数 像不像路径」、不看参数位置、不看目标存在与否——即将创建的路径也得到合法的 Win32 形态。随包的 PATH-MATRIX 以约 100 条探针逐格实跑了方言 × 执行层的全量 矩阵,缺口全部开票在案(WinuxCmd#1244、rubash#850、niu-git#18)。

argv byte-for-byte verbatim: MSYS_NO_PATHCONV does not exist here

Git Bash's MSYS runtime heuristically rewrites path-looking arguments before spawn: /c becomes C:\, a non-existent /foo expands into the Git install path, the pattern in git grep "/fn main" gets silently rewritten. That is why every veteran user has had to learn the MSYS_NO_PATHCONV / MSYS2_ARG_CONV_EXCL incantations — the existence of an off switch is itself the official admission of the problem.

niubash has no MSYS runtime; those two environment variables do not exist here and do not need to. The boundary is a written core invariant — "what must be verbatim is not converted": commit messages, branch names, quoted operands, the syntax data of grep/sed/awk/find (patterns, script text, -name-style values), /cygdrive/…, /dev/*, the environment-variable channel, every non-path-shaped word — all reach the child process byte-for-byte. The only switch, __RUBASH_ARGV_DIALECT=legacy, is a documented dialect selector (an emergency fallback to the old behavior), not a life raft for "heuristics that might guess wrong".

中文原文 · Chinese original

$PWD 恒为 POSIX 形态(如 /d/repo)是配套裁决:这是 nvm 死循环根因(rubash#537)修复的同一决定,生态脚本对 $PWD 的 POSIX 假设因此成立。

uname reporting MSYS_NT: a deliberate compatibility persona

On Windows, uname -s reports MSYS_NT-… (mapped to MINGW64_NT- and friends in the MSYS2 manner when MSYSTEM is set), with $OSTYPE=msys and $MACHTYPE=x86_64-pc-msys. This is not an impersonation of a runtime but a compatibility persona (rubash#154): plenty of ecosystem scripts use case "$(uname -s)" matching MINGW*|MSYS*|CYGWIN* as their branching gate, and the persona opens those gates so scripts take their most-tested paths. Under the label there is no MSYS2 runtime at all: no msys-2.0.dll, no cygwin1.dll, no POSIX argv conversion layer inside child processes.

One-line contrast: Git Bash = MSYS2 runtime + real bash; niubash = native engine + compatibility persona. The persona is disclosed everywhere (--help / --identity), and RUBASH_IDENTITY=native switches to the honest native face (uname -s → Windows_NT, OSTYPE=windows).

Honesty section: the write-side gap, tracked in the open

A boundary contract is not zero gaps. The bare-drive-root gap in write-side commands is being closed: #1230 audited 15 write-side commands, with mkdir's true write-mode support (#1233) the most urgent. That is the value of a boundary contract — gaps are enumerable, ticketable and fixable, instead of scattered in the fog of "sometimes it behaves like Linux, sometimes not". Every cell closed turns another PATH-MATRIX cell green.