You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
PowerShell needs shortcut vars for [IO.Path]::PathSeparator and [IO.Path]::DirectorySeparatorChar #5340
I'm sorry but having to drop down to the .NET Framework to access the characters needed to separate paths and the appropriate slash to use to separate directories in a path needs to be built-in and more accessible to non-programmer scripters (admins).
I mean sort of the whole point of PS Core is to be cross-platform. Seems like we should grease the skips for this set of scenarios.
Steps to reproduce
You need to append a path to $env:PATH in a cross platform way. As an admin scripter, how do you do that?
Expected behavior
Admin scripter should be able to find a "built-in" variable in the about_automatic_variables and then use the variable like so:
$env:Path+="${PSPathSep}${myNewPath}"# Or they want a simple string literal join of path segments$exePath="${InstallDir}${PSDirSep}foo.exe"
Actual behavior
Admin scripter has to go look up some obtuse "programmy" syntax:
changed the title [-]PowerShell needs shortcut consts for [IO.Path]::PathSeparator and [IO.Path]::DirectorySeparatorChar[/-][+]PowerShell needs shortcut vars for [IO.Path]::PathSeparator and [IO.Path]::DirectorySeparatorChar[/+]on Nov 4, 2017
I'm not excited about adding another $PS variable because the prefix is ugly, but an obvious variable is likely to collide with a user variable.
Join-Path is what non-developers use for directories - it could take a parameter like uh, well, not -Path, but uh, hmm :) PathSeparator is poorly named I guess.
Yeah, in retrospect maybe if would have been nicer if this was a bit more "encapsulated" e.g. $PS:PathSep and $PS:DirSep. So rather than a litter of global variables, there is a single PS variable drive. For comparison, Python uses os.pathsep and os.sep for these.
Jason Shirk (@lzybkr): Join-Path doesn't really need a new parameter, because it already uses the platform-appropriate separator - and extending its purpose to also append to $env:PATH seems like a poor fit.
For the compose-a-single-filesystem-path scenario, Join-Path is therefore sufficient (especially now that it accepts a variable number of components):
$exePath=Join-Path$InstallDirfoo.exe
That said, having something like $ps:DirSep too can't hurt.
(Arguably, it should be $ps:PathSeparator - you're not just separating directories but path components, which can be directories or files; it's hard to say whether following .NET's misguided nomenclature is more important than getting it right.)
As for the adding-an-entry-to-$env:PATH scenario:
Indeed, [IO.Path]::PathSeparator is poorly named.
It's been suggested before (I forget where) that PowerShell provide an array-based way of accessing and modifying $env:PATH, which would hide the platform-specific PATH-environment-variable-entry-separator (; on Windows, : on Unix).
# Wishful thinking: treat $env:PATH as an array$ps:Path+=$myNewPath
Again, also having something like $ps:PathVarSep can't hurt.
I would discourage using path separator variables because it creates a bad coupling when concatenating paths since one has to know if the path already ends with a path separator or not to avoid having 2 path separators or none. Join-Path or [System.IO.Path]::Combine takes care of all that. And even if you used the 'wrong' slashes, all .NET core methods are agnostic of it. Should you have some other API that is picky, then just pass your path string to Get-Item and PowerShell will correct your slashes.
You're referring to the directory separator and yeah Join-Path is a reasonable alternative. Path separator ; or : is not handled by Join-Path or the other .NET methods you mention - not that an admin should have to resort to using .NET methods for such a simple, common scenario.
I bumped into this under PowerShell Core for prepending to $env:PATH. I ended up using ($binPath, $env:PATH) -join [IO.Path]::PathSeparator but I only knew that thanks to a friend who knows PowerShell and C#. 😄
I mean, obviously you don't want to append your path to the string if it's already there (right?), so a function that can join path variables automatically using the right character for the and preventing duplicates with knowledge of the OS case-sensitivity would be really helpful...
I like the idea. I don't particularly like the chosen names there... there's not enough semantic distance between the two, I think.
Besides, this would still require manual handling of duplicate entries in the path -- I think Joel Bennett (@Jaykul)'s proposal for a cmdlet/function that handles this stuff consistently in a neat package would be ideal, and would help integrate PowerShell into the different operating systems more smoothly. 😄
I don't think this functionality should be tacked onto Join-Path, which is about composing a (set of) path(s) - something quite distinct from managing the entries of an environment variable containing a list of paths.
As Joel Bennett (@Jaykul) and Rain Sallow (/u/ta11ow) (@vexx32) suggest, having a dedicated cmdlet to manage such variables makes sense, and if that cmdlet covers all desired modification scenarios to such a variable, the need to expose the platform-specific separator directly goes away.
That is, the following 3 operations are needed when it comes to environment variables such as $env:PATH- but note that the same logic is applicable to similar env. variables such as $env:CLASSPATH:
Append a new directory path.
Prepend a new directory path.
Remove a directory path.
As suggested, in the case of appending or prepending, it makes sense to avoid duplicate entries and by default do nothing, if the target directory is already present.
This can be supplemented with -Force to ensure moving an existing entry to the beginning or end.
Here's what the syntax diagram for such a cmdlet could look like (name negotiable):
The default -Target would be Process, i.e., updating the variable for the current process only.
Targeting User or Machine to effect persistent env.-var. changes would work on Windows only, because the underlying .NET method that would be used - Environment.SetEnvironmentVariable - doesn't support this on macOS and Linux.
In fact, it quietly ignores these values- see https://github.com/dotnet/corefx/issues/32685
Reacted by Rain Sallow (/u/ta11ow) and Dmitriy Ivanov
Thanks, Keith Hill (@rkeithhill) - I'd lost track of that RFC early on; good to see all the discussion there.
Just briefly (I'll take the discussion over to the RFC):
I now think that a single, generic list-string manipulation is called for that doesn't itself set environment variables, but allows manipulation of their values, if they're list-based, with an -AsDirList switch to use the platform-abstracted [IO.Path]::PathSeparator - see Introduce a cmdlet for manipulation of list strings #7975
Conversely, that would mean keeping any list-parsing functionality out of any *-Environment cmdlets and, more fundamentally, I think there shouldn't even be new *-Environment cmdlets - instead, the existing environment-variable drive provider should be enhanced to support persistence.
Even though with the above the need to surface [IO.Path]::PathSeparator explicitly in a PowerShell-friendly manner is diminished, I do still see value in also doing that, and, as stated before, I'm with you on surfacing such variables via a new namespace / drive, as discussed in #4394 (comment) - TBD whether with a catch-all drive such as $ps:... or - perhaps taking a page out of Python's book - grouped thematically so that $os:... could contain $os.PathVariableSeparator ([IO.Path]::PathSeparator) as well as $os.DirectorySeparator ([IO.Path]::DirectorySeparatorChar), and possibly more (names negotiable, but I don't think CoreFx's confusing nomenclature is worth keeping in this instance).
microsoft-github-policy-service commented on Nov 16, 2023
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
microsoft-github-policy-service commented on Nov 16, 2023
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
microsoft-github-policy-service commented on Nov 23, 2023
Activity
[-]PowerShell needs shortcut consts for [IO.Path]::PathSeparator and [IO.Path]::DirectorySeparatorChar[/-][+]PowerShell needs shortcut vars for [IO.Path]::PathSeparator and [IO.Path]::DirectorySeparatorChar[/+]lzybkr commentedon Nov 4, 2017
I'm not excited about adding another
$PSvariable because the prefix is ugly, but an obvious variable is likely to collide with a user variable.Join-Pathis what non-developers use for directories - it could take a parameter like uh, well, not-Path, but uh, hmm :) PathSeparator is poorly named I guess.rkeithhill commentedon Nov 4, 2017
Yeah, in retrospect maybe if would have been nicer if this was a bit more "encapsulated" e.g.
$PS:PathSepand$PS:DirSep. So rather than a litter of global variables, there is a single PS variable drive. For comparison, Python usesos.pathsepandos.sepfor these.mklement0 commentedon Nov 5, 2017
Keith Hill (@rkeithhill):
Indeed; déjà vu: #4394 (comment)
Jason Shirk (@lzybkr):
Join-Pathdoesn't really need a new parameter, because it already uses the platform-appropriate separator - and extending its purpose to also append to$env:PATHseems like a poor fit.For the compose-a-single-filesystem-path scenario,
Join-Pathis therefore sufficient (especially now that it accepts a variable number of components):That said, having something like
$ps:DirSeptoo can't hurt.(Arguably, it should be
$ps:PathSeparator- you're not just separating directories but path components, which can be directories or files; it's hard to say whether following .NET's misguided nomenclature is more important than getting it right.)As for the adding-an-entry-to-$env:PATH scenario:
Indeed,
[IO.Path]::PathSeparatoris poorly named.It's been suggested before (I forget where) that PowerShell provide an array-based way of accessing and modifying
$env:PATH, which would hide the platform-specific PATH-environment-variable-entry-separator (;on Windows,:on Unix).Again, also having something like
$ps:PathVarSepcan't hurt.lzybkr commentedon Nov 5, 2017
I like the idea of supporting
+=on "Path" like variables - that's worth some more thinking.Today, an environment variable is a string, but if we could have strongly typed environment variables, we could implement operators like
+and[].bergmeister commentedon Nov 5, 2017
I would discourage using path separator variables because it creates a bad coupling when concatenating paths since one has to know if the path already ends with a path separator or not to avoid having 2 path separators or none.
Join-Pathor[System.IO.Path]::Combinetakes care of all that. And even if you used the 'wrong' slashes, all .NET core methods are agnostic of it. Should you have some other API that is picky, then just pass your path string toGet-Itemand PowerShell will correct your slashes.rkeithhill commentedon Nov 5, 2017
You're referring to the directory separator and yeah Join-Path is a reasonable alternative. Path separator
;or:is not handled by Join-Path or the other .NET methods you mention - not that an admin should have to resort to using .NET methods for such a simple, common scenario.bergmeister commentedon Nov 5, 2017
OK, I see what you mean now in the case of path separators. Sorry about my loose use of vocabulary.
rkeithhill commentedon Nov 5, 2017
No problem. As some folks have pointed out already, the naming leaves something to be desired.
brettcannon commentedon Sep 14, 2018
I bumped into this under PowerShell Core for prepending to
$env:PATH. I ended up using($binPath, $env:PATH) -join [IO.Path]::PathSeparatorbut I only knew that thanks to a friend who knows PowerShell and C#. 😄Jaykul commentedon Oct 6, 2018
Wouldn't it be better to have a function?
I mean, obviously you don't want to append your path to the string if it's already there (right?), so a function that can join path variables automatically using the right character for the and preventing duplicates with knowledge of the OS case-sensitivity would be really helpful...
SteveL-MSFT commentedon Oct 6, 2018
How about a
-Separatorparameter toJoin-Paththat defaults toDirectory, but an enum that also hasPaththat maps to[IO.Path]::PathSeparator?vexx32 commentedon Oct 7, 2018
I like the idea. I don't particularly like the chosen names there... there's not enough semantic distance between the two, I think.
Besides, this would still require manual handling of duplicate entries in the path -- I think Joel Bennett (@Jaykul)'s proposal for a cmdlet/function that handles this stuff consistently in a neat package would be ideal, and would help integrate PowerShell into the different operating systems more smoothly. 😄
mklement0 commentedon Oct 8, 2018
I don't think this functionality should be tacked onto
Join-Path, which is about composing a (set of) path(s) - something quite distinct from managing the entries of an environment variable containing a list of paths.As Joel Bennett (@Jaykul) and Rain Sallow (/u/ta11ow) (@vexx32) suggest, having a dedicated cmdlet to manage such variables makes sense, and if that cmdlet covers all desired modification scenarios to such a variable, the need to expose the platform-specific separator directly goes away.
Update: See PowerShell/PowerShell-RFC#92 for a preexisting discussion on this.
That is, the following 3 operations are needed when it comes to environment variables such as
$env:PATH- but note that the same logic is applicable to similar env. variables such as$env:CLASSPATH:Append a new directory path.
Prepend a new directory path.
Remove a directory path.
As suggested, in the case of appending or prepending, it makes sense to avoid duplicate entries and by default do nothing, if the target directory is already present.
This can be supplemented with
-Forceto ensure moving an existing entry to the beginning or end.Here's what the syntax diagram for such a cmdlet could look like (name negotiable):
The default
-Targetwould beProcess, i.e., updating the variable for the current process only.Targeting
UserorMachineto effect persistent env.-var. changes would work on Windows only, because the underlying .NET method that would be used -Environment.SetEnvironmentVariable- doesn't support this on macOS and Linux.In fact, it quietly ignores these values- see https://github.com/dotnet/corefx/issues/32685
rkeithhill commentedon Oct 8, 2018
Um, like this? PowerShell/PowerShell-RFC#92
mklement0 commentedon Oct 8, 2018
Thanks, Keith Hill (@rkeithhill) - I'd lost track of that RFC early on; good to see all the discussion there.
Just briefly (I'll take the discussion over to the RFC):
I now think that a single, generic list-string manipulation is called for that doesn't itself set environment variables, but allows manipulation of their values, if they're list-based, with an-AsDirListswitch to use the platform-abstracted[IO.Path]::PathSeparator- see Introduce a cmdlet for manipulation of list strings #7975Conversely, that would mean keeping any list-parsing functionality out of any
*-Environmentcmdlets and, more fundamentally, I think there shouldn't even be new*-Environmentcmdlets - instead, the existing environment-variable drive provider should be enhanced to support persistence.Even though with the above the need to surface
[IO.Path]::PathSeparatorexplicitly in a PowerShell-friendly manner is diminished, I do still see value in also doing that, and, as stated before, I'm with you on surfacing such variables via a new namespace / drive, as discussed in #4394 (comment) - TBD whether with a catch-all drive such as$ps:...or - perhaps taking a page out of Python's book - grouped thematically so that$os:...could contain$os.PathVariableSeparator([IO.Path]::PathSeparator) as well as$os.DirectorySeparator([IO.Path]::DirectorySeparatorChar), and possibly more (names negotiable, but I don't think CoreFx's confusing nomenclature is worth keeping in this instance).microsoft-github-policy-service commentedon Nov 16, 2023
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
microsoft-github-policy-service commentedon Nov 16, 2023
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
microsoft-github-policy-service commentedon Nov 16, 2023
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
microsoft-github-policy-service commentedon Nov 23, 2023
This issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes.