8

I'm more familiar with Bash than with Powershell and sometimes I get confused by the latter's object model.

Looking at the documentation of Get-FileHash, it seems that there are 3 ways of specifying the input:

  • Get-FileHash [-Path]
  • Get-FileHash [-LiteralPath]
  • Get-FileHash [-InputStream]

The first two take file names, the third a stream of data.

Now, Get-ChildItem -File seems to output System.IO.FileInfo objects, judging from what Get-Member says:

$ Get-ChildItem -File | Get-Member
TypeName: System.IO.FileInfo

And yet the pipeline Get-ChildItem -File | Get-FileHash works correctly. My question is, what is the mechanism that allows converting System.IO.FileInfo to the type of inputs expected by Get-FileHash?

danidiaz
  • 26,936
  • 4
  • 45
  • 95
  • 1
    Maybe [Retrieving default property names](https://stackoverflow.com/q/38137864/205233) has the answer ... – Filburt Feb 11 '20 at 20:31
  • 1
    @Filburt: The linked post is about (display) _formatting_, whereas this question is about _parameter binding_. – mklement0 Feb 12 '20 at 15:00
  • 1
    See also a blog post about [Type Conversion Magic](https://devblogs.microsoft.com/powershell/understanding-powershells-type-conversion-magic/) by Lee Holmes (MS PowerShell dev team). – ives Mar 08 '20 at 19:19

3 Answers3

10

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.

mklement0
  • 382,024
  • 64
  • 607
  • 775
3

From the About Functions Advanced Parameters documentation, ValueFromPipelineByPropertyName argument section:

The ValueFromPipelineByPropertyName argument indicates that the parameter accepts input from a property of a pipeline object. The object property must have the same name or alias as the parameter.

For example, if the function has a ComputerName parameter, and the piped object has a ComputerName property, the value of the ComputerName property is assigned to the function's ComputerName parameter.

Update: Originally linked the wrong source code file. The correct Get-FileHash source code is here. As @mklement0 correctly answered the Get-ChildItem cmdlet outputs an object with a PSPath property, which makes this work.

kuujinbo
  • 9,272
  • 3
  • 44
  • 57
  • 1
    Strangely, path doesn't work. `[pscustomobject]@{path='there'} | get-filehash` – js2010 Feb 11 '20 at 22:04
  • 4
    @js2010 in Powershell 5.1, `Accept pipeline input: False` for parameter `Path`. See also `(Get-Command Get-FileHash).Definition` – JosefZ Feb 11 '20 at 22:37
  • 1
    @JosefZ - thanks for the correction! `[pscustomobject]@{LiteralPath='PATH'} | Get-FileHash` – kuujinbo Feb 11 '20 at 23:46
0

It looks like it uses the pspath property.

dir there | select pspath | get-filehash

Algorithm  Hash                                                                Path
---------  ----                                                                ----
SHA256     44723DD4D0E0D46A3C7FA8ACA254B61C27B6B5789F96177E82C80700409F1535    C:\users\...
js2010
  • 23,033
  • 6
  • 64
  • 66