_[Updated based on @lzybkr's feedback below.]_
Use of [CmdletBinding()]
and parameter attributes in Filter
functions requires special considerations, and it is easy to create functions that invariably fail at runtime, in obscure fashion.
Perhaps it's possible to detect such cases at parse time.
Note that a tempting reason to use [CmdletBinding()]
is to benefit from its automatic enforcement of allowing only arguments for explicitly declared parameters to be passed.
Filter foo1 { [CmdletBinding()] param() $_ }
Filter foo2 { param([Parameter(Position=0)] $Bar) $_ }
1 | foo1; 1 | foo2
The reasons these two Filter
functions invariably fail when invoked is that - due to using [CmdletBinding()]
and/or parameter attributes - at least one pipeline-binding parameter must be defined explicitly.
This requirement makes the use of these attributes in Filter
functions impractical, as you may as well define a regular Function
function with an explicit process
block at that point, which offers the added flexibility of begin
and end
blocks.
Ideally, report an error during the parsing stage.
foo1 : The input object cannot be bound to any parameters for the command either
because the command does not take pipeline input or the input and its properties
do not match any of the parameters that take pipeline input.
...
foo2 : The input object cannot be bound to any parameters for the command either
because the command does not take pipeline input or the input and its properties
do not match any of the parameters that take pipeline input.
That is, the function definitions succeeded, but any attempt to use them invariably fails.
Note that if you use just [CmdletBinding()]
and no param()
block, the functions don't break, but [CmdletBinding()]
is quietly ignored.
PowerShell Core v6.0.2 on macOS 10.13.3
PowerShell Core v6.0.2 on Ubuntu 16.04.3 LTS
PowerShell Core v6.0.2 on Microsoft Windows 10 Pro (64-bit; v10.0.15063)
Windows PowerShell v5.1.15063.674 on Microsoft Windows 10 Pro (64-bit; v10.0.15063)
If the body does not use named blocks, the body is a process
block if you used the filter
keyword or the body is an end
block if you used the function
keyword.
The following should all work similarly:
function f1 {
param([Parameter(ValueFromPipeline)]$InputObject)
process { $InputObject }
}
filter f2 {
param([Parameter(ValueFromPipeline)]$InputObject)
$InputObject
}
filter f3 {
param([Parameter(ValueFromPipeline)]$InputObject)
process { $InputObject }
}
So your actual issue is that a function with a process
block (named or unnamed when using filter
) must specify a parameter with ValueFromPipeline
if the function uses cmdlet binding.
Like many other semantic errors involving the Parameter attribute, this could be detected at parse time, but hasn't in part because resolution of types and attributes was deferred until first invocation to allow a script to load necessary assemblies.
That said, Parameter
and CmdletBinding
could be special cased - symbol resolution could happen in two stages - an early pass (like what is needed for classes) can be performed during parsing, and a second time upon invocation.
Most helpful comment
If the body does not use named blocks, the body is a
process
block if you used thefilter
keyword or the body is anend
block if you used thefunction
keyword.The following should all work similarly:
So your actual issue is that a function with a
process
block (named or unnamed when usingfilter
) must specify a parameter withValueFromPipeline
if the function uses cmdlet binding.Like many other semantic errors involving the Parameter attribute, this could be detected at parse time, but hasn't in part because resolution of types and attributes was deferred until first invocation to allow a script to load necessary assemblies.
That said,
Parameter
andCmdletBinding
could be special cased - symbol resolution could happen in two stages - an early pass (like what is needed for classes) can be performed during parsing, and a second time upon invocation.