Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PowerShell starts prepending file paths with Microsoft.PowerShell.Core\FileSystem::

I am experiencing the following behavior in PowerShell that I cannot explain and find cumbersome.

I am working in an arbitrary directory and the path of the directory is shown in the prompt:

PS C:\Users\Rene\AppData\Local\Temp>

Also, get-location reports the "correct" path:

PS C:\Users\Rene\AppData\Local\Temp> get-location

Path
----
C:\Users\Rene\AppData\Local\Temp

I then type mkdir xyz | cd in order to create a directory and change the working directory into this new directory:

PS C:\Users\Rene\AppData\Local\Temp> mkdir xyz | cd

All of a sudden, the path in the prompt is prefixed with Microsoft.PowerShell.Core\FileSystem:::

PS Microsoft.PowerShell.Core\FileSystem::C:\Users\Rene\AppData\Local\Temp\xyz>

This change is also reflected with get-location:

PS Microsoft.PowerShell.Core\FileSystem::C:\Users\Rene\AppData\Local\Temp\xyz> get-location

Path
----
Microsoft.PowerShell.Core\FileSystem::C:\Users\Rene\AppData\Local\Temp\xyz

What is going on here and how can I turn that prefix off?

like image 406
René Nyffenegger Avatar asked Jan 24 '26 15:01

René Nyffenegger


1 Answers

In reverse order:

How can I turn that prefix off?

Easy, use explicit pipeline binding!

mkdir xyz |cd -Path {$_.FullName}

What is going on here?

Great question! What you see here is a side-effect of how the provider cmdlets (Get-ChildItem, Get-Item, Set-Location etc.) implements pipeline binding.

When you call New-Item (which is what mkdir does) against the FileSystem provider, it returns an object (corresponding to the newly created file or directory) that has a bunch of hidden properties that PowerShell uses to keep track of items across providers - these can be discovered with Get-Member -Force:

PS C:\> Get-Item .|Get-Member PS* -MemberType NoteProperty -Force


   TypeName: System.IO.DirectoryInfo

Name          MemberType   Definition
----          ----------   ----------
PSChildName   NoteProperty string PSChildName=C:\
PSDrive       NoteProperty PSDriveInfo PSDrive=C
PSIsContainer NoteProperty bool PSIsContainer=True
PSParentPath  NoteProperty string PSParentPath=
PSPath        NoteProperty string PSPath=Microsoft.PowerShell.Core\FileSystem::C:\
PSProvider    NoteProperty ProviderInfo PSProvider=Microsoft.PowerShell.Core\FileSystem

When you construct a pipeline statement with a provider cmdlet (such as Set-Location/cd for example) downstream, it uses the provider-qualified PSPath value to figure bind the input object.

This can be observed with Trace-Command:

PS C:\> Trace-Command -Expression {Get-Item .|Set-Location} -Name ParameterBinding,MemberResolution -PSHost

Which results in (I've removed the details for Get-Item for brevity):

DEBUG: ParameterBinding Information: 0 : BIND NAMED cmd line args [Set-Location]
DEBUG: ParameterBinding Information: 0 : BIND POSITIONAL cmd line args [Set-Location]
DEBUG: ParameterBinding Information: 0 : BIND cmd line args to DYNAMIC parameters.
DEBUG: ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet [Set-Location]
DEBUG: ParameterBinding Information: 0 : CALLING BeginProcessing
DEBUG: ParameterBinding Information: 0 : CALLING BeginProcessing
DEBUG: ParameterBinding Information: 0 : BIND PIPELINE object to parameters: [Set-Location]
DEBUG: ParameterBinding Information: 0 :     PIPELINE object TYPE = [System.IO.DirectoryInfo]
DEBUG: ParameterBinding Information: 0 :     RESTORING pipeline parameter's original values
DEBUG: ParameterBinding Information: 0 :     Parameter [Path] PIPELINE INPUT ValueFromPipeline NO COERCION
DEBUG: ParameterBinding Information: 0 :     BIND arg [C:\\] to parameter [Path]
DEBUG: ParameterBinding Information: 0 :         BIND arg [C:\\] to param [Path] SKIPPED
DEBUG: ParameterBinding Information: 0 :     Parameter [Path] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION
DEBUG: MemberResolution Information: 0 :     Lookup
DEBUG: MemberResolution Information: 0 :         "Path" NOT present in type table.
DEBUG: MemberResolution Information: 0 :         Adapted member: not found.
DEBUG: ParameterBinding Information: 0 :     Parameter [StackName] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION
DEBUG: MemberResolution Information: 0 :     Lookup
DEBUG: MemberResolution Information: 0 :         "StackName" NOT present in type table.
DEBUG: MemberResolution Information: 0 :         Adapted member: not found.
DEBUG: ParameterBinding Information: 0 :     Parameter [LiteralPath] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION
DEBUG: MemberResolution Information: 0 :     Lookup
DEBUG: MemberResolution Information: 0 :         "LiteralPath" NOT present in type table.
DEBUG: MemberResolution Information: 0 :         Adapted member: not found.
DEBUG: MemberResolution Information: 0 :     Lookup
DEBUG: MemberResolution Information: 0 :         Found PSObject instance member: PSPath.
DEBUG: ParameterBinding Information: 0 :     BIND arg [Microsoft.PowerShell.Core\FileSystem::C:\] to parameter [LiteralPath]
DEBUG: ParameterBinding Information: 0 :         BIND arg [Microsoft.PowerShell.Core\FileSystem::C:\] to param [LiteralPath] SUCCESSFUL
DEBUG: ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet [Set-Location]
DEBUG: ParameterBinding Information: 0 : CALLING EndProcessing
DEBUG: ParameterBinding Information: 0 : CALLING EndProcessing

As you can see, PowerShell foregoes binding C:\ to Set-Locations -Path parameter, as somehow binding the PSPath property value to -LiteralPath is more appropriate?!

The reason for that is that the -LiteralPath parameter is aliased to PSPath, as can be seen by digging a bit with Get-Command:

PS C:\> (Get-Command Set-Location).Parameters['LiteralPath'] |Select Aliases

Aliases
-------
{PSPath}

The real reason why pipeline bindings for provider cmdlets is implemented this way, is twofold:

  1. Binding a "provider-native" string representation directly to Path might have unintended consequences related to globbing
    • Get-Item -Path 'a[bcd]' and Get-Item -LiteralPath 'a[bcd]' are two wildly different queries for example
  2. Binding on the provider-qualified PSPath value means that we can switch location to a different provider without losing automatic binding:
    • ie. PS Cert:\> $aFile |Get-Content just works
like image 58
Mathias R. Jessen Avatar answered Jan 26 '26 21:01

Mathias R. Jessen