Skip to content

PowerShell needs shortcut vars for [IO.Path]::PathSeparator and [IO.Path]::DirectorySeparatorChar #5340

Closed

Description

@rkeithhill

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:

$env:Path += "$([IO.Path]::PathSeparator)${myNewPath}"
$exePath = "${InstallDir}$([IO.Path]::DirectorySeparatorChar)foo.exe"

We can do better than this.

Environment data

PS Core 6

Activity

  1. 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
  2. lzybkr commented on Nov 4, 2017

    @lzybkr
    Contributor

    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.

  3. rkeithhill commented on Nov 4, 2017

    @rkeithhill
    CollaboratorAuthor

    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.

  4. mklement0 commented on Nov 5, 2017

    @mklement0
    Contributor

    Keith Hill (@rkeithhill):

    it would have been nicer if this was a bit more "encapsulated" e.g. $PS:PathSep and $PS:DirSep

    Indeed; déjà vu: #4394 (comment)

    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 $InstallDir foo.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.

  5. lzybkr commented on Nov 5, 2017

    @lzybkr
    Contributor

    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 [].

  6. bergmeister commented on Nov 5, 2017

    @bergmeister
    Contributor

    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.

  7. rkeithhill commented on Nov 5, 2017

    @rkeithhill
    CollaboratorAuthor

    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.

  8. bergmeister commented on Nov 5, 2017

    @bergmeister
    Contributor

    OK, I see what you mean now in the case of path separators. Sorry about my loose use of vocabulary.

  9. rkeithhill commented on Nov 5, 2017

    @rkeithhill
    CollaboratorAuthor

    No problem. As some folks have pointed out already, the naming leaves something to be desired.

  10. added
    Issue-Discussionthe issue may not have a clear classification yet. The issue may generate an RFC or may be reclassif
    on Nov 9, 2017
  11. brettcannon commented on Sep 14, 2018

    @brettcannon

    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#. 😄

  12. Jaykul commented on Oct 6, 2018

    @Jaykul
    Contributor

    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...

  13. SteveL-MSFT commented on Oct 6, 2018

    @SteveL-MSFT
    Member

    How about a -Separator parameter to Join-Path that defaults to Directory, but an enum that also has Path that maps to [IO.Path]::PathSeparator?

    $env:PATH = Join-Path $env:PATH $binPath -Separator Path
  14. vexx32 commented on Oct 7, 2018

    @vexx32
    Collaborator

    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. 😄

  15. mklement0 commented on Oct 8, 2018

    @mklement0
    Contributor

    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 -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):

    Update-PathEnvironmentVariable `
      [[-Name]=<string>='PATH'] [-Remove <string[]>] [-Prepend <string[]>] [-Append <string[]>] `
      [-Target = 'Process'|'User'|'Machine'] `
      [-Force]

    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

  16. rkeithhill commented on Oct 8, 2018

    @rkeithhill
    CollaboratorAuthor

    having a dedicated cmdlet to manage such variables makes sense

    Um, like this? PowerShell/PowerShell-RFC#92

  17. mklement0 commented on Oct 8, 2018

    @mklement0
    Contributor

    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).

  18. microsoft-github-policy-service commented on Nov 16, 2023

    @microsoft-github-policy-service
    Contributor

    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.

  19. microsoft-github-policy-service commented on Nov 16, 2023

    @microsoft-github-policy-service
    Contributor

    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.

  20. microsoft-github-policy-service commented on Nov 16, 2023

    @microsoft-github-policy-service
    Contributor

    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.

  21. microsoft-github-policy-service commented on Nov 23, 2023

    @microsoft-github-policy-service
    Contributor

    This issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-Discussionthe issue may not have a clear classification yet. The issue may generate an RFC or may be reclassifResolution-No ActivityIssue has had no activity for 6 months or more

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions