Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does "Get-ChildItem -File | Get-FileHash" work?

Tags:

powershell

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?

like image 764
danidiaz Avatar asked Feb 11 '20 20:02

danidiaz


2 Answers

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:

  • Via the online help topic:

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.
PS> & {
        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


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.

like image 82
mklement0 Avatar answered Oct 26 '22 01:10

mklement0


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.

like image 28
kuujinbo Avatar answered Oct 26 '22 01:10

kuujinbo