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:
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
?
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.
-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:
Programmatically:
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With