Powershell: Handle ~ in PATH

Created on 8 Jan 2020  路  17Comments  路  Source: PowerShell/PowerShell

Steps to reproduce

  1. Put any executable (ex. myExe) at the following path: ~/foo/
  2. Add that path to your PATH
$env:PATH = "~/foo$([System.IO.Path]::PathSeparator)$env:PATH"

Try to tab complete your executable or just run myExe

Expected behavior

The executable myExe is run.

This works just fine in bash and zsh and things like .NET global tools rely on the ~ support in the PATH so we should really fix this.

caveat

Bash seems to do more - it can handle variables inside of the PATH but that seems like overkill and I can't think of any example that uses that "feature".

We handle tilda pretty much everywhere else, we should be able to support it in application resolution.

Actual behavior

The term 'myExe' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

Environment data

Name                           Value
----                           -----
PSVersion                      7.0.0-daily.20200108
PSEdition                      Core
GitCommitId                    7.0.0-daily.20200108
OS                             Darwin 18.7.0 Darwin Kernel Version 18.7.0: Sun Dec  1 18:59:03 PST 2019; root:xnu-4903.278.19~1/RELEASE_X86_64
Platform                       Unix
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0鈥
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
Issue-Enhancement Resolution-Fixed WG-Engine-Providers

Most helpful comment

We've had a bit more discussion on this:

  • The OS cares about PATH, since it's used for execvp(3)
  • The OS does not expand ~ in that context
  • Some utilties, like which also do not expand ~
  • But some others do, like nano/vim

So some questions I think are:

  • Should PowerShell expand ~ for its own PATH discovery purposes?
  • Should PowerShell expand ~ in arguments to native commands?
  • Should PowerShell expand ~ in PATH/other env vars before passing them through to subprocesses?
  • Should PowerShell do similar things with env vars within PATH etc. ?

My own thinking is that we should do the first and nothing more, since the first is convenient without breaking things, but the others all have the possibility of creating serious headaches.

All 17 comments

unfortunately, this means that you can't run .NET global tools in PowerShell without manual PATH changes

Part of this issue looks like a natural result of how Windows handles variables in PATH -- it supports cmd-type variables like %USERPROFILE% but essentially nothing else.

I guess you could add special handling for additional tokens, but there's always the question of how far you want to go. At the end of the day no matter what you do, the dotnet tools using this kind of thing would only be usable from powershell, and essentially nowhere else in Windows.

Yes, modifying system env variable in non-standard way is not good because other applications will do not understand this.
Also we can create file or directory with "~" name that could lead to path conflict.

I'm not asking for modification of the environment variable. I'm saying that on command discovery, we also treat ~ as the home directory.

Resolving variables is out of scope for this issue and I don't think I've ever seen PATH abuse like that so we probably don't need to have that discussion until an ask comes around.

@TylerLeonhardt You modify PATH in your repo steps so now I don't understand the issue. Do you mean that PATH already has "~" in PATH before pwsh started and we should handle this path?

Yes. .NET global tools add the following path to the PATH:

~/.dotnet/tools

They do this on macOS by putting a file here:

PS > cat /etc/paths.d/dotnet-cli-tools
~/.dotnet/tools

This is at the OS level and not within PowerShell. This path on the PATH works fine in bash (meaning my .NET global tools do resolve just fine) but in PowerShell, it can't understand the ~ so global tools just don't resolve.

Shouldn't dotnet global tools be doing a platform check and using %USERPROFILE% instead for that purpose on Windows?

@vexx32 Not sure about the behavior on Windows, but he's talking about the macOS behavior.

If bash supports expanding ~ for command resolution then I agree that PowerShell should do the same (at least on the platforms that typically support it).

There seems to be a lot of confusion on the ask, so I'm going to try to clarify. @TylerLeonhardt feel free to correct anything I get wrong:

When using macOS (and probably *nix, someone will need to confirm):

  • Installing a dotnet global tool adds ~/.dotnet/tools to $env:PATH
  • Bash already handles resolving ~ in $env:PATH
  • PowerShell does not resolve ~ in $env:PATH for any platform
  • But should support it's resolution on platforms that already support it outside of PowerShell

Oh, I missed that. Thanks for clarifying! Yeah, on Unix systems that support ~ natively, PS should be supporting it in $env:PATH as well, absolutely.

on Unix systems that support ~ natively

For some more context, we looked into it and some other tools that care about PATH on macOS didn't seem to be ~ or env var aware. There's a natural question of "whose API is PATH?", but it seems that bash has a particular behaviour here that's worth at least partially replicating

The problem is here. [System.IO.Directory]::Exists("~") returns false. We would need to handle ~ expansion. How much of Bash behavior would we want to replicate? The right place to perform the expansion is probably here

Iterative makes sense here... Considering we have a pretty important scenario (.NET Global Tools) that uses just a regular tilda, that's probably a good place to start. In other words, supporting the first 2 scenarios on that Bash link:

~
The value of $HOME

~/foo
$HOME/foo

We've had a bit more discussion on this:

  • The OS cares about PATH, since it's used for execvp(3)
  • The OS does not expand ~ in that context
  • Some utilties, like which also do not expand ~
  • But some others do, like nano/vim

So some questions I think are:

  • Should PowerShell expand ~ for its own PATH discovery purposes?
  • Should PowerShell expand ~ in arguments to native commands?
  • Should PowerShell expand ~ in PATH/other env vars before passing them through to subprocesses?
  • Should PowerShell do similar things with env vars within PATH etc. ?

My own thinking is that we should do the first and nothing more, since the first is convenient without breaking things, but the others all have the possibility of creating serious headaches.

From https://github.com/dotnet/cli/issues/9321 I am not sure that this will work on MacOs and zsh.

:tada:This issue was addressed in #11552, which has now been successfully released as v7.0.0-rc.2.:tada:

Handy links:

Was this page helpful?
0 / 5 - 0 ratings

Related issues

andschwa picture andschwa  路  3Comments

JohnLBevan picture JohnLBevan  路  3Comments

garegin16 picture garegin16  路  3Comments

aragula12 picture aragula12  路  3Comments

rudolfvesely picture rudolfvesely  路  3Comments