Why does “Get-ChildItem -File | Get-FileHash” work?

[*]

The System.IO.FileInfo / System.IO.DirectoryInfo instances output by PowerShell cmdlets have a .PSPath property[*] that contains the instances’ fully qualified path, which is the full file-system path prefixed by the PS provider name (e.g., Microsoft.PowerShell.Core\FileSystem::C:\windows).

File-processing cmdlets such as Get-FileHash have a -LiteralPath parameter which has an alias name of -PSPath.

Because a -LiteralPath parameter (typically) accepts input from the pipeline by property name, input objects that have a .PSPath property automatically bind to it, by virtue of the PSPath parameter alias name.

As an aside:

  • File-processing cmdlets also have a -Path parameter, which interprets its arguments as wildcard expressions, not as literal paths.

    • When you pipe path strings to such cmdlets, they bind to -Path, which notably means that they are indeed interpreted as wildcards – while this will typically not matter, because most literal paths do not contain wildcard metacharacters, it does with paths that contain [ and ], which are then misinterpreted; avoiding this misinterpretation requires escaping them as `[ and `], as shown in this answer.
  • Due to a bug in Windows PowerShell (since fixed in PowerShell (Core) 7+), Get-FileHash, specifically, doesn’t accept strings via the pipeline – see this answer for details.


How to discover this behavior:

parameter description

  • Programmatically:

    • Note: Get-Help Get-FileHash -Parameter LiteralPath | Select-Object name, aliases, pipelineinput works too in this case, but this approach is generally restricted to target commands that come with MAML-based help files, and even those that do can have help files that are out of sync with the actual command definition.
& {
  Get-Command $args[0] | % Parameters | % $args[1] |
  Select-Object Name, Aliases, @{
    n = 'Accepts pipeline input';
    e = { $(if ($_.Attributes.ValueFromPipeline) { 'by value' }), $(if ($_.Attributes.ValueFromPipelineByPropertyName) { 'by property name' }) -join ', ' -replace '^, ' }
  }
} Get-FileHash LiteralPath

Output:

Name        Aliases      Accepts pipeline input
----        -------      ----------------------
LiteralPath {PSPath, LP} by property name

[*] It is PowerShell’s file-system provider that adds this property, among others. All PowerShell providers decorate their output items this way, such as the Microsoft.Win32.RegistryKey instances output by the registry provider. The underlying .NET types do not have it. See this answer for more information.

Leave a Comment