Oh, that is tricky and needs a bit of an essay. Sorry. I’ll restrict most of it to just Cygwin-derived shells, which covers the vast majority of the normal Windows shell use cases today. The minority use cases I talk about at the bottom.
Cygwin has a DLL called cygwin1.dll
. MSYS2 has a DLL called msys-2.0.dll
which is similar so I’ll ignore it. DLLs behave somewhat similarly to .so
shared libraries … the TEXT (code) and DATA segments are mapped into the virtual address space of the process they are linked to (analog: ld.so
) or loaded into (analog: dlopen
). But DLLs can have well-defined entry and exit functions that automatically get called whenever they are loaded into a process. What cygwin1.dll
does is load a shared memory object when it is attached to any process, so that all processes linked with cygwin1.dll
see the same shared memory object.
All the normal GNU binaries like bash.exe
, dash.exe
(the POSIX shell), ls.exe
and make.exe
and all the GNU autoconf tools are linked with cygwin1.dll
. And these GNU binaries are compiled using normal POSIX C code like fopen
.
fopen
is a good example. cygwin1.dll
implements fopen("/etc/passwd", "r")
by consulting cygwin1.dll/../etc/fstab
to find the mapping between /etc/passwd
and the Windows file system; for speed the mapping is stashed into the shared memory object. The really important point is that each cygwin1.dll
has its own shared memory object which gives its own file system, its own POSIX locks, and so on.
On my machine:
- Cygwin64 Terminal has
/etc/passwd -> Z:\cygwin64\etc\passwd
- MSYS2 Terminal has
/etc/passwd -> Z:\msys64\etc\passwd
- Git Bash’s embedded MSYS2 has
/etc/passwd -> C:\Program Files\Git\etc\passwd
- Diskuv OCaml’s embedded MSYS2 has
/etc/passwd -> C:\Users\beckf\AppData\Local\Programs\DiskuvOCaml\tools\MSYS2\etc\passwd
- vcpkg’s embedded MSYS2 has
/etc/passwd -> Z:\source\cpkgs\vcpkg\downloads\tools\msys2\9a1ec3f33446b195\etc\passwd
cygwin1.dll
also proxies the getenv("PATH")
function. It takes all of the PATH entries from Windows and translates them into the /
Unix-y file system.
These /
reverse mappings are relative to the specific cygwin1.dll
. So if I were in Cygwin64 Terminal I would see:
Z:\cygwin64\etc\passwd -> /etc/passwd
Z:\msys64\etc\passwd -> /cygdrive/z/msys64/etc/passwd
C:\Program Files\Git\etc\passwd -> /cygdrive/c/Program Files/Git/etc/passwd
- and so on
And if I were in MSYS2 Terminal I would see the same pattern (except it doesn’t use /cygdrive
):
Z:\cygwin64\etc\passwd -> /z/cygwin64/etc/passwd
Z:\msys64\etc\passwd -> /etc/passwd
C:\Program Files\Git\etc\passwd -> /c/Program Files/Git/etc/passwd
- and so on
So there is no loss of fidelity moving between PATHs on Windows and “Unix”.
Finally, if you understood everything I wrote above, can you predict what happens when:
-
Your Windows PATH is a seemingly reasonable:
C:\Program Files\Git\bin;C:\WINDOWS\system32;C:\WINDOWS
-
You are inside the MSYS2 Terminal shell Z:\msys64\usr\bin\bash.exe
.
-
You run a script with a shebang:
#!/usr/bin/env bash
echo hi
Which bash will be used? MSYS2 Terminal’s or Git’s?
How about if you run the following?
#!/bin/bash
echo hi
Minority use cases: WSL2 shells
Very very annoyingly if I type bash
on the Command Prompt I immediately enter WSL2 Ubuntu Linux. I don’t know when that was introduced, and I don’t know the mechanism how that works. All I know is that I’ve had shell scripts magically invoke Ubuntu Linux from Cygwin-derived shells.
Yet another complexifier.
The problem of invoking the correct shell is solved as long as every package uses #!/bin/sh
rather than #!/usr/bin/env sh
. Good luck trying to get that to happen, especially retroactively! Or we can do what the POSIX spec suggests … let one place (the installer) determine the correct shebang. And as a bonus, nixos can magically start working as well.
As a stop-gap, I have wrappers around common OCaml tools (so far dune.exe
and opam.exe
) that constructs sanitized PATHs for Diskuv OCaml native Windows users. See crossplatform-functions.sh and with_dkml.ml. Those hacks are the only reason why this isn’t high priority for Windows.