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
Implement ~username expansion on Windows and Linux #12387
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.
Reacted by Michael Klement and Flavien MICHALECZEK
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}
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.
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?
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.
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")
Activity
doctordns commentedon Apr 20, 2020
You can use the "~" character just fine, like this:
And to set that path in advance, you can do this:
Seems to me you have what you are asking for already.
rkitover commentedon Apr 20, 2020
Thomas Lee (@doctordns) no this is a different thing,
~is the profile path of the current user, while~userwould be the profile path of a specific user, e.g.~bob/would beC:\Users\bob.doctordns commentedon Apr 20, 2020
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\userprobably would not work for obvious reasons, also~\User@domainis also possibly problematic.What is the use case?
Seems to me you could just do a variation on this:
mklement0 commentedon Apr 20, 2020
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; compareGet-Item filesystem::~toGet-Item variable::~. In short: theFileSystemprovider 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 *.txtpasses matching filenames on Linux, whilesomeutility.exe *.txton Windows performs no expansion and passes*.txtverbatim; 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\jdoeor~jdoe@foo.comsupport 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)
~jdoethrough as-is, if a localjdoeuser 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.
doctordns commentedon Apr 20, 2020
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?
mklement0 commentedon Apr 20, 2020
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), theFileSystemprovider would have to recognize and resolve~<user>paths - whether they're quoted or not, becauseGet-Item ~works the same asGet-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.mklement0 commentedon Dec 6, 2023
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
kshandzshalso support variable-based expansions, e.g.$user = 'jdoe'; /bin/echo ~$userBy 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 tojdoe's home dir. (but only - as usual - if either followed by no additional characters or an unquoted/)'~jdoe',"~$user")