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
Output redirect from mkdir to cd shows unexpected internal prefix #17949
I am experiencing an unexpected behaviour when I created a directory and tried to redirect the output (folder-object) to cd to navigate into the created folder directly.
Expected behavior
PS E:\> mkdir Test | cdPS E:\Test>
Actual behavior
PS E:\> mkdir Test | cdPS Microsoft.PowerShell.Core\FileSystem::E:\Test>
Error details
No response
Environment data
Name Value
---------
PSVersion 7.2.6
PSEdition Core
GitCommitId 7.2.6
OS Microsoft Windows 10.0.17763
Platform Win32NT
PSCompatibleVersions {1.0,2.0,3.0,4.0…}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
This sets the actions for piped objects as follows
If they have a property named Path, use that as the -Path parameter.
If not, but if they have a property named LiteralPath , PSPath or LP use that as the -LiteralPath parameter (there cannot be a literalpath if path already exists)
If they have no such property, convert the object to a string and use that as the -Path parameter
(LiteralPath doesn't process wildcards which affects [ xx ] with the file system provider, other providers might consider * and/or ? to be legal in a path)
The second part is that if you create a new directory or get one with Get-Item / Get-ChildItem (or one of their aliases) the System.IO.DirectoryInfo object returned doesn't have a property named path the name used is FullName , but PowerShell adds a PSPath property to files and directories,
Putting the two parts together means Get-Item PowerShell | cd Is equivalent to
Which is seldom what we want (but cd / Set-LocationDoes allow the current location to be something which does not have a PSDrive, - for example cd Microsoft.PowerShell.Core\Registry::HKEY_CLASSES_ROOT. )
If we convert to a string before piping into cd, the string becomes the -Path parameter, and
$p = Get-Item PowerShell
Set-Location $p
will assume $P is the -path parameter, and convert it to a string. Doing that returns the fullname property, and works as expected, i.e. cd $p is equivalent to doing Set-Location -path $p.ToString()
We can skip storing the variable, so cd (mkdir test) creates the directory and passes it as the -path parameter which is then converted to a string holding the full name.
$p | command and command $pshould, ideally, do the same thing and they don't here, cd $p does what is expected and $p | cd works differently. This annoyance could be fixed by either or both:
Making Fullname an alias for -Path in commands where it is a problem. This could be done with proxy function (but most people won't go down that route).
Adding an alias property .Path to file and directory objects (it is possible to define and load a types.PS1xml file to do this, but again most people won't)
I don't know if the effort : reward ratio means it has never been worth doing it, or whether either of those would have side-effects we don't want. I can't invent a case where there are side effects, but that doesn't mean there aren't any.
James O'Neill (@jhoneill) That's quite an investigation and write-up! I propose two extra solutions which may be simpler:
At the end of Set-Location, if the path can be simplified, set location to the simplified one
Since this is mostly a visual bug, simplify location if possible only in the prompt
I vote for option 2, as it is less likely to be a breaking change if some edge case relies on the current location being in this weird long form.
thomasley You can test if function Prompt { (Resolve-Path Get-Location) -replace $HOME, '~' } solves your issue, if not then try replacing Resolve-Path with some other function.
You can't say with 100% certainty what the path should be simplified to
Should get-item $env:temp | cd resolve to TEMP: or C:\users\me\AppData\Local\Temp
And it is possible (if unlikely) that someone wants to do
> get-item hklm: | cd
And later be able to do
> cd '..\HKEY_Classes_root'
This isn't possible with cd HKLM: - but with that cd \ works and with get-item hklm: | cd it fails.
If a script has cd Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\ I wouldn't want Set-location to assume that HKLM: was what was wanted.
But I'm reasonably happy that assuming get-itemPowerShell | cd meant change to the full name not to the PSPath instead of assuming we want the PSPath..
I'm not sure masking having a different directory is a good idea it would warn you why (e.g.) start $pwd doesn't like the directory and so on.
Good analysis, James O'Neill (@jhoneill), but just as an aside, regarding one of the workarounds: That Get-Item . | Set-Location and Set-Location -LiteralPath (Get-Item .) bind the -LiteralPath parameter differently - solely based on the incidental aspect of whether the value came from the pipeline or was passed as an argument - is problematic in its own right:
Michael Klement (@mklement0) yes, interesting point .
ValueFromPipelineByPropertyName doesn't act as Value From Argument By Property name. If there were such a parameter option I have multiple places where it would have simplified function bodies, (if an object is piped use its ID property, but if something passed as an argument, check if it has an ID property, and if so use that in place of the whole object)
But I'd want them kept separate.
I think, also, one piped object can go to many parameters with ByPropertyName, but an argument must bind to exactly 1 parameter.
Activity
jhoneill commentedon Aug 23, 2022
This quirk is expected and correct (but annoying).
TLDR Use
cd (mkdir test)ormkdir test | % tostring | cdinstead.First part of the explanation is that many PowerShell commands including
Set-Location(aliasCD) have parameters like thisThis sets the actions for piped objects as follows
Path, use that as the-Pathparameter.LiteralPath,PSPathorLPuse that as the-LiteralPathparameter (there cannot be aliteralpathifpathalready exists)-Pathparameter(
LiteralPathdoesn't process wildcards which affects[ xx ]with the file system provider, other providers might consider*and/or?to be legal in a path)The second part is that if you create a new directory or get one with
Get-Item/Get-ChildItem(or one of their aliases) theSystem.IO.DirectoryInfoobject returned doesn't have a property namedpaththe name used isFullName, but PowerShell adds aPSPathproperty to files and directories,Putting the two parts together means
Get-Item PowerShell | cdIs equivalent toWhich is seldom what we want (but
cd/Set-LocationDoes allow the current location to be something which does not have a PSDrive, - for examplecd Microsoft.PowerShell.Core\Registry::HKEY_CLASSES_ROOT. )If we convert to a string before piping into cd, the string becomes the
-Pathparameter, andwill assume
$Pis the-pathparameter, and convert it to a string. Doing that returns thefullnameproperty, and works as expected, i.e.cd $pis equivalent to doingSet-Location -path $p.ToString()We can skip storing the variable, so
cd (mkdir test)creates the directory and passes it as the-pathparameter which is then converted to a string holding the full name.$p | commandandcommand $pshould, ideally, do the same thing and they don't here,cd $pdoes what is expected and$p | cdworks differently. This annoyance could be fixed by either or both:Fullnamean alias for-Pathin commands where it is a problem. This could be done with proxy function (but most people won't go down that route)..Pathto file and directory objects (it is possible to define and load a types.PS1xml file to do this, but again most people won't)I don't know if the effort : reward ratio means it has never been worth doing it, or whether either of those would have side-effects we don't want. I can't invent a case where there are side effects, but that doesn't mean there aren't any.
dkaszews commentedon Aug 23, 2022
James O'Neill (@jhoneill) That's quite an investigation and write-up! I propose two extra solutions which may be simpler:
Set-Location, if the path can be simplified, set location to the simplified oneI vote for option 2, as it is less likely to be a breaking change if some edge case relies on the current location being in this weird long form.
thomasley You can test if
function Prompt { (Resolve-Path Get-Location) -replace $HOME, '~' }solves your issue, if not then try replacingResolve-Pathwith some other function.jhoneill commentedon Aug 23, 2022
Dominik Kowalski (@dkaszews) :-)
You can't say with 100% certainty what the path should be simplified to
Should
get-item $env:temp | cdresolve to TEMP: or C:\users\me\AppData\Local\TempAnd it is possible (if unlikely) that someone wants to do
And later be able to do
This isn't possible with
cd HKLM:- but with thatcd \works and withget-item hklm: | cdit fails.If a script has
cd Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\I wouldn't want Set-location to assume that HKLM: was what was wanted.But I'm reasonably happy that assuming
get-itemPowerShell | cdmeant change to the full name not to the PSPath instead of assuming we want the PSPath..I'm not sure masking having a different directory is a good idea it would warn you why (e.g.)
start $pwddoesn't like the directory and so on.iSazonov commentedon Aug 24, 2022
It is fundamentally follow PowerShell provider conceptions.
Right fix should rather be in Formatting System or prompt function.
Dup #10522
jhoneill commentedon Aug 24, 2022
I think we should close this one as duplicate and take discussion to 10522
mklement0 commentedon Aug 24, 2022
Good analysis, James O'Neill (@jhoneill), but just as an aside, regarding one of the workarounds: That
Get-Item . | Set-LocationandSet-Location -LiteralPath (Get-Item .)bind the-LiteralPathparameter differently - solely based on the incidental aspect of whether the value came from the pipeline or was passed as an argument - is problematic in its own right:jhoneill commentedon Aug 24, 2022
Michael Klement (@mklement0) yes, interesting point .
ValueFromPipelineByPropertyName doesn't act as Value From Argument By Property name. If there were such a parameter option I have multiple places where it would have simplified function bodies, (if an object is piped use its ID property, but if something passed as an argument, check if it has an ID property, and if so use that in place of the whole object)
But I'd want them kept separate.
I think, also, one piped object can go to many parameters with ByPropertyName, but an argument must bind to exactly 1 parameter.