Skip to content

Implement ~username expansion on Windows and Linux #12387

Closed

Description

In UNIX shells it is common to refer to the home directories of specific users as ~username, this is very convenient for many things.

Currently this doesn't work on either Windows or Linux in powershell.

Following up on discussion in #11386.

Activity

  1. doctordns commented on Apr 20, 2020

    @doctordns
    Collaborator

    You can use the "~" character just fine, like this:

    PS C:\Windows> Set-Location -Path ~
    PS C:\foo> Get-ChildItem -path ~\xml
    ...

    And to set that path in advance, you can do this:

    $Provider = Get-PSProvider Filesystem
    $Provider.Home="c:\foo"

    Seems to me you have what you are asking for already.

  2. rkitover commented on Apr 20, 2020

    @rkitover
    ContributorAuthor

    Thomas Lee (@doctordns) no this is a different thing, ~ is the profile path of the current user, while ~user would be the profile path of a specific user, e.g. ~bob/ would be C:\Users\bob.

  3. doctordns commented on Apr 20, 2020

    @doctordns
    Collaborator

    That is going to be hard, I suspect.

    If you want ~<username>, what format is user name going to take and how would you differentiate a domain vs local user? ~\domain\user probably would not work for obvious reasons, also ~\User@domain is also possibly problematic.

    What is the use case?

    Seems to me you could just do a variation on this:

    $Cred = Get-Credential    # Enter the username/password
    $Location = Invoke-Command -computername localhost -credential $Cred -scriptblock {cd ~;Get-location}
  4. mklement0 commented on Apr 20, 2020

    @mklement0
    Contributor

    Rafael Kitover (@rkitover), can you please make the following clear in the original post?
    We're talking about more fully emulating ~ expansion for native utilities (external programs), so that it works like in POSIX-like shells.

    Thomas Lee (@doctordns), the use case is to have a convenient shorthand for referring to a different user's home directory path.

    (For PowerShell commands, ~ works fundamentally differently: in the native-utility emulation, it is the engine that expands the ~, whereas in the case of PowerShell commands the command / provider receives the ~ as-is, and it is then its responsibility to interpret the ~ as its home location. That is, to provider-based PowerShell commands ~ does not necessarily refer to $HOME (the file-system), and to other commands ~ has no special meaning. Note that some providers do not define home locations; compare Get-Item filesystem::~ to Get-Item variable::~. In short: the FileSystem provider specifically would have to implement the ~<username> logic.)

    While the desired behavior on Unix-like platforms is clear (make it work like in POSIX-like shells such as bash), Thomas Lee (@doctordns) has a point about needing to define the appropriate behavior on Windows.

    Note that one option is not to support this on Windows.
    While the asymmetry would be somewhat unfortunate, we already have asymmetries: on Windows, when calling external programs, ~ is not expanded, and globbing (wildcard expansion) is not performed; e.g., someutility *.txt passes matching filenames on Linux, while someutility.exe *.txt on Windows performs no expansion and passes *.txt verbatim; then again, perhaps that asymmetry is also worth revisiting.

    If we do want to support this on Windows (which is preferable for cross-platform consistency), I suppose one potential approach could be:

    • support mere user names only, i.e.. without a domain component; e.g, ~jdoe, but not ~foo\jdoe or ~jdoe@foo.com

    • support the names of local users and those in the current user's logon domain, if applicable (don't know how expensive such a lookup would be)

      • Note that POSIX-like shells pass ~jdoe through as-is, if a local jdoe user doesn't exist, so in order to emulate this behavior we'd need to know if the given user name refers to an existing user.
    • if a custom home directory is configured for a given target user, that would have to be taken into account as well.

  5. doctordns commented on Apr 20, 2020

    @doctordns
    Collaborator

    Michael Klement (@mklement0) makes a good point and I see what the OP is driving at. I am not familiar with Linux sufficiently!

    In the cases I've seen, the '~' is part of a file path that is provided to the -Path parameter (or similar). So it makes sense for consistency if nothing else is if the ~ as provided to one of the path parameters is handled by PowerShell (ie without any username syntax). That is the current behaviour.

    Could PowerShell use the --% parameter to pass the ~ to the program/command without expansion for the program to handle (eg: someothercommand --% -Path ~\markmike\foo). That would just pass the '~' as is?

    Is this a case where --% style logic might work?

  6. mklement0 commented on Apr 20, 2020

    @mklement0
    Contributor

    While you can use --% to pass an unquoted ~ as-is, all you need to do is to quote the ~ when calling an external program, which tells the engine to treat it literally: /bin/echo '~' passes ~ verbatim.

    (Generally, --% is a stopgap with many limitations, especially on Unix. You could argue that --% should do the opposite and that --% should be the required opt-in for emulating the platform-native shell's features (~ expansion, globbing), but that ship has sailed.)

    In POSIX-like shells ~ is purely a shell construct, and it is the shell that expands it before target programs see the paths - the target programs are not expected to interpret ~ themselves.

    On Unix, PowerShell already emulates this POSIX-shell function with unquoted ~ alone (though doesn't always recognize it properly - see #11386), and this issue asks that the emulation be made more complete, by also supporting ~<user> - do note that there's no path separator between ~ and <user>.

    if we limit ourselves to supporting ~ expansion for external programs on Unix - as we have so far - there's nothing more to do.


    If we want to bring this functionality to Windows too:

    • To extend the existing ~ support, which is limited to provider-based cmdlets only (and can refer to non-file-system locations), the FileSystem provider would have to recognize and resolve ~<user> paths - whether they're quoted or not, because Get-Item ~ works the same as Get-Item '~' - that'll have to remain a difference to the native emulation.

    • Also bringing ~ support to calling external programs on Windows - which isn't currently supported, just as globbing isn't - would amount to a breaking change, unfortunately.

      • It would change the behavior of existing command lines that have unquoted ~ arguments; e.g., someutility.exe ~ would no longer pass ~ verbatim; in addition to the technical change, this is problematic because~ is primarily a Unix construct and on Windows cannot generally be assumed to be a stand-in for the user's home directory.

      • The same applies if, for consistency, we tried to bring native globbing to Windows as well: someutility.exe .txtcurrently passes.txt` verbatim and would suddenly start passing multiple file-name arguments.

  7. added
    WG-Engine-Providersbuilt-in PowerShell providers such as FileSystem, Certificates, Registry, etc.
    on Apr 22, 2020
  8. mklement0 commented on Dec 6, 2023

    @mklement0
    Contributor

    As for the Windows angle: support for ~ in general is now being discussed in #20750 (comment)


    One subtlety, discovered by rhubarb-geek-nz, is that ksh and zsh also support variable-based expansions, e.g. $user = 'jdoe'; /bin/echo ~$user

    By contrast, the POSIX spec POSIX spec supports only verbatim usernames and so does bash (/bin/echo ~jdoe)

    While one option is to limit PowerShell to emulating the POSIX-compliant / Bash behavior and therefore allowing verbatim usernames only, the support for variables makes the feature more flexible and therefor usable in scripts rather than just interactively.

    To also support variables, the logic would have to be (as implied by the usual quoting rules of POSIX-compatible shells), with $user = 'jdoe':

    • ~jdoe, ~$user, ~"$user" should expand to jdoe's home dir. (but only - as usual - if either followed by no additional characters or an unquoted /)
    • Any other forms shouldn't (e.g., '~jdoe', "~$user")
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-Enhancementthe issue is more of a feature request than a bugWG-Engine-Providersbuilt-in PowerShell providers such as FileSystem, Certificates, Registry, etc.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions