考虑以下脚本:
function g
{
[CmdletBinding()]
param
(
[parameter(ValueFromPipelineByPropertyName = $true)]$x,
[parameter(ValueFromPipelineByPropertyName = $true)]$y,
[parameter(ValueFromPipelineByPropertyName = $true)]$z
)
process
{
$retval = @{psbp=@{};mibp=@{};x=$x;y=$y;z=$z}
$PSBoundParameters.Keys |
% { $retval.psbp.$_ = $PSBoundParameters.$_ }
$PSCmdlet.MyInvocation.BoundParameters.Keys |
% { $retval.mibp.$_ = $PSCmdlet.MyInvocation.BoundParameters.$_}
return New-Object psobject -Property $retval
}
}
$list = (New-Object psobject -Property @{x=1;z=3}),
(New-Object psobject -Property @{x=$null;y=2} )
$list |
g |
Select bp,x,y,z |
ft -AutoSize
运行脚本会产生以下输出:
psbp mibp x y z
---- ---- - - -
{z, x} {z, x} 1 3
{y, z, x} {y, z, x} 2
这似乎表明,$PSBoundParameters
和$PSCmdlet.MyInvocation.BoundParameters
都包含到目前为止绑定的所有参数的累积。
我非常确定第一步绑定了$x
和$z
,第二步绑定了$x
和$y
,但是我还没有找到以编程方式检索该细节的方法。
如何确定仅在当前管道步骤中绑定的参数?
为什么我关心这个?某些参数验证比使用参数集,ValidateScript()
等语言功能更复杂。验证必须在函数体内执行。有时,最好将未绑定参数视为与将$null
传递给同一参数在语义上不同。通常通过询问$PSBoundParameters
来检测结合参数。如果只传递管道上的单个参数对象,则此方法可以正常工作。但是,如果使用管道管道参数对象列表,则由于上述脚本所显示的问题,该检测会被挫败。这违反了最小的原则,因为当调用者碰巧通过将相同的对象传递给它时,在foreach
循环内工作正常的函数的行为会有很大的不同。
我可以通过在foreach
内调用受影响的函数来解决这个问题,但我宁愿为所有调用解决问题,而不是完全放弃管道。< / p>
答案 0 :(得分:2)
您可以记住begin
块中命令行所有的所有参数,然后在process
块的末尾,您可以从当前输入对象所限定的参数中清除$PSBoundParameters
:
function g
{
[CmdletBinding()]
param
(
[parameter(ValueFromPipelineByPropertyName = $true)]$x,
[parameter(ValueFromPipelineByPropertyName = $true)]$y,
[parameter(ValueFromPipelineByPropertyName = $true)]$z
)
begin
{
$CommandLineBoundParameters=@($PSBoundParameters.Keys)
}
process
{
...
@($PSBoundParameters.Keys) |
? { $CommandLineBoundParameters -notcontains $_ } |
% { [void]$PSBoundParameters.Remove($_) }
}
}