Skip to content

Output redirect from mkdir to cd shows unexpected internal prefix #17949

Closed

Description

@OnkelMato

Prerequisites

Steps to reproduce

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 | cd
PS E:\Test>

Actual behavior

PS E:\> mkdir Test | cd
PS 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

Visuals

image

Activity

  1. jhoneill commented on Aug 23, 2022

    @jhoneill

    This quirk is expected and correct (but annoying).

    TLDR Use cd (mkdir test) or mkdir test | % tostring | cd instead.

    First part of the explanation is that many PowerShell commands including Set-Location (alias CD) have parameters like this

        [Parameter(ParameterSetName='Path', Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
        [string]$Path,
    
        [Parameter(ParameterSetName='LiteralPath', Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
        [Alias('PSPath','LP')]
        [string]$LiteralPath
    

    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

     $p = Get-Item PowerShell
     Set-Location -LiteralPath $p.PSPath
    

    Which is seldom what we want (but cd / Set-Location Does 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 $p should, 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:

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

  2. dkaszews commented on Aug 23, 2022

    @dkaszews
    Contributor

    James O'Neill (@jhoneill) That's quite an investigation and write-up! I propose two extra solutions which may be simpler:

    1. At the end of Set-Location, if the path can be simplified, set location to the simplified one
    2. 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.

  3. jhoneill commented on Aug 23, 2022

    @jhoneill

    Dominik Kowalski (@dkaszews) :-)

    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.

  4. iSazonov commented on Aug 24, 2022

    @iSazonov
    Collaborator

    It is fundamentally follow PowerShell provider conceptions.
    Right fix should rather be in Formatting System or prompt function.

    Dup #10522

  5. ghost removed
    Needs-TriageThe issue is new and needs to be triaged by a work group.
    on Aug 24, 2022
  6. jhoneill commented on Aug 24, 2022

    @jhoneill

    I think we should close this one as duplicate and take discussion to 10522

  7. mklement0 commented on Aug 24, 2022

    @mklement0
    Contributor

    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:

  8. jhoneill commented on Aug 24, 2022

    @jhoneill

    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.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions