Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PATH gets mangled when using direnv from git-bash on Windows #253

Open
irontoby opened this issue Feb 20, 2017 · 25 comments
Open

PATH gets mangled when using direnv from git-bash on Windows #253

irontoby opened this issue Feb 20, 2017 · 25 comments
Labels

Comments

@irontoby
Copy link

Hello,

Installing the official git release for Windows will also install the git-bash.exe shell. Rather than an emulation layer, I believe this is bash plus lots of the standard Linux tools which have been compiled directly for Windows (it seems to be based off the Mingw-w64 project).

Within this shell, it mounts the Windows C: drive as /c and converts all the Windows paths to their Bash equivalents. For example my PATH right now looks something like this:

/c/Users/tobyj/bin:/mingw64/bin:/usr/local/bin:/usr/bin:/bin:/mingw64/b
in:/usr/bin:/c/ProgramData/Oracle/Java/javapath:/c/Windows/system32:/c/
Windows:/c/Windows/System32/Wbem:/c/Program Files/MariaDB 10.1/bin:/c/P
rogram Files (x86)/Yarn/bin:/usr/bin/vendor_perl:/usr/bin/core_perl

I downloaded the Windows version of direnv (direnv.windows-amd64.exe) and put it in a folder in my path. Then I added eval "$(direnv hook bash)" to my .bashrc. However when I create an .envrc and try either PATH_add node_modules/.bin or export PATH=$PWD/node_modules/.bin:$PATH, it appears that direnv is converting my $PATH to use Windows-style paths instead:

C:\devs\myproj\node_modules\.bin;C:\Users\tobias.johnson\bin;C:\Progr
am Files\Git\mingw64\bin;

Is there a way to get the Windows .exe to use Bash-style paths and not try to convert them? Or is there a config setting I need to change to get this behavior?

@zimbatm
Copy link
Member

zimbatm commented Feb 20, 2017

Hi, thanks for the comprehensive report.

It's a long shot but can you try the latest release? It's now built with go 1.8 which has improved the windows path support.

I produce the windows binaries because it's easy to do so with Go but I must admit of not having tested it.

@irontoby
Copy link
Author

Nope, no luck. Knowing that it's not really tested/supported on Windows helps though; if I get a chance I'll try to dig in a bit more and figure out whether it's something that can be worked around. Thanks!

@zimbatm
Copy link
Member

zimbatm commented Feb 20, 2017

Cool. I'm very interested in having good Windows support, it's just that I am not using it day-to-day. Maybe also have a look at the existing windows issues, it might give you some more clues: https://github.com/direnv/direnv/issues?q=is%3Aopen+is%3Aissue+label%3AWindows

@markwest1
Copy link

markwest1 commented Mar 16, 2017

@irontoby, I am experiencing the same issue and would be glad to help as well. If I can do any testing or coding (I've no production experience writing golang, but love to tinker with it).

@zimbatm
Copy link
Member

zimbatm commented Mar 16, 2017

Can you try to run direnv expand_path /c/Users/tobyj/bin? If it converts it to the C:\ format it means that the golang stdlib does something funky.

Basically I think we would have to replace or expand the path module in the standard library to understand more of the windows path intricacies. Maybe windows needs multiple path evaluation modes depending on whenever direnv is being run in cmd.exe, bash, PowerShell, ...

@markwest1
Copy link

image
image

I'll take a look in the direnv code ... should I start in exec.go ? Or maybe upwards in the call stack?

@zimbatm
Copy link
Member

zimbatm commented Mar 16, 2017

That's another issue we'll hit with Windows. Because there is a white space in the $PWD, bash interprets it as multiple arguments. Make sure to put double quotes around $PWD, or any variables in general.

@markwest1
Copy link

I don't think the line in the shell output, /usr/bin/bash: line 195 : C:/Program: No such file or directory is caused by the line in the .envrc file, expand_path $PWD because the same output is generated when the line is changed to expand_path "$PWD":
image
image
But I agree with your assertion that double quotes will be necessary around bash variable evaluation statements like $PWD in .envrc files

@zimbatm
Copy link
Member

zimbatm commented Mar 16, 2017

It's possible some of the stdlib.sh isn't escaped properly. Can you simplify the .envrc to just echo SUCCESS?

@markwest1
Copy link

image

@xn
Copy link

xn commented Apr 19, 2017

I'm about to go down this path. Any movement on this?

@markwest1
Copy link

Nope, sorry.

zimbatm added a commit that referenced this issue Apr 23, 2017
The function would fail if direnv lived in a path which contains
whitespaces (aka Windows C:\\Program Files)

See #253
@zimbatm
Copy link
Member

zimbatm commented Apr 23, 2017

@markwest1 @xn can you try the current master? I think I fixed this particular issue.

@mcandre
Copy link

mcandre commented Jun 29, 2017

I get a similar error when using direnv 2.11.3. Tried both 64-bit and 32-bit direnv, with a Git Bash context.

If I use a blank .envrc, or a .envrc with 100% commented out code, my Git Bash shell works fine.

If I try to source rsvm from .envrc, then the resulting Git Bash shell environment can no longer execute commands. ls, which, and type all complain about missing paths, due to how direnv mangles paths when run in a cygwin environment.

One way to help alleviate this, is for direnv to determine whether it is running in a cygwin/unix/linux/mingw/msys/Git Bash/Windows Subsystem for Linux context vs. a native Windows/Command Prompt/PowerShell context.

If uname -a matches MINGW*, direnv is running in a cygwin context, and should take care to use cygwin (Unix-style) paths. If uname -a does not match MINGW*, direnv is likely running in another Unix-like context, and should still use Unix-style paths. Finally, if uname is not found, direnv is likely running in a Windows context, and should use Windows-style paths.

There are plenty of other ways to distinguish environments, but uname is especially helpful as an environment determinant, because it does not rely on any particular shell interpreter, so it can be used in Git Bash, Windows Subsystem for Linux bash, zsh, ksh, posh, Command Prompt, PowerShell, etc. So even the most ridiculously custom setups can still reliably distinguish between Unix and Windows pathing with the same command.

A detection scheme like that would go a long way toward resolving compatibility issues for direnv across Unix, Windows, and cygwin environments.

@zimbatm
Copy link
Member

zimbatm commented Jun 30, 2017

@mcandre I agree, right now direnv is relying on the golang stdlib path package but I think that it's not sufficient for windows support as windows is not a single target.

If someone is willing to implement a path package that works in all these environments it would go a long way to have direnv supported on windows. Right now I suspect that direnv will only work properly inside of the WSL.

@xn
Copy link

xn commented Jun 30, 2017

BTW, I got it working.

@xn
Copy link

xn commented Jun 30, 2017

in git-bash

$ which direnv
/c/Users/IEUser/bin/direnv
$ cat ~/.bashrc
eval "$(direnv hook bash)"

@zimbatm
Copy link
Member

zimbatm commented Jun 30, 2017

was the solution to remove whitespaces from the path?

@xn
Copy link

xn commented Jun 30, 2017

yes

@markwest1
Copy link

markwest1 commented Jul 5, 2017

@zimbatm the issue is resolved when using direnv in git-bash (MINGW64) on windows. Thank you; sorry it took so long to get back to you.
image

@zimbatm
Copy link
Member

zimbatm commented Jul 5, 2017

no worries. if you still have whitespaces try direnv v2.12.2, it should also fix the issue

@vagabundmw
Copy link

vagabundmw commented Feb 24, 2019

I'm still having issues with the current version of direnv in Windows Git Bash. For my use case I want to extend the PATH variable, which also contains spaces. As a crude workaround I patched the prompt hook. So instead of calling eval "$(direnv hook bash)" I'm using:

_direnv_hook() {
  local previous_exit_status=$?;
  eval "$(MSYS_NO_PATHCONV=1 "direnv.exe" export bash | sed 's|export PATH=|export _X_DIRENV_PATH=|g')";
  if [ -n "$_X_DIRENV_PATH" ]; then
    _X_DIRENV_PATH=$(cygpath -p "$_X_DIRENV_PATH")
    export "PATH=$_X_DIRENV_PATH"
    unset _X_DIRENV_PATH
  fi
  return $previous_exit_status;
};


if ! [[ "$PROMPT_COMMAND" =~ _direnv_hook ]]; then
  PROMPT_COMMAND="_direnv_hook;$PROMPT_COMMAND"
fi

This hook feeds PATH assignments through cygpath, which converts the Windows format in direnv's output to the format Bash expects.

Also, since direnv is a non-MSYS executable, MSYS will translate other environment variables (according to these rules) before calling it. This was undesirable for my scenario so I disable it by setting MSYS_NO_PATHCONV.

Edit:
As a side note, the stdlib path_add functions do not work due to similar Windows vs. Bash format conversion issues, because direnv is called by path_add. So, alas, I had to avoid the stdlib.

@sph3rex
Copy link

sph3rex commented Feb 14, 2020

Additionally in the latest version, even an empty .envrc on win10 with gitbash triggers this error:

direnv: error write 3: The handle is invalid.

@tonyg
Copy link

tonyg commented Jan 4, 2024

Here's a similar hack, replacing eval $(direnv hook bash) in .bashrc:

export _unmangle_direnv_names='PATH'
_unmangle_direnv_paths() {
    for k in $_unmangle_direnv_names; do
        eval "$k=\"\$(/usr/bin/cygpath -p \"\$$k\")\""
    done
}
eval "$(direnv hook bash | sed -e 's@export bash)@export bash)\
_unmangle_direnv_paths@')"

If path-unmangling is required for other variables, they can be added to _unmangle_direnv_names in each .envrc.

@doronbehar
Copy link

So instead of calling eval "$(direnv hook bash)" I'm using:

Your _direnv_hook was very helpful, Thanks for sharing!

Edit:
As a side note, the stdlib path_add functions do not work due to similar Windows vs. Bash format conversion issues, because direnv is called by path_add. So, alas, I had to avoid the stdlib.

I just tested PATH_add with direnv 2.34.0 and with your hook and it worked like a charm.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

No branches or pull requests

9 participants