测试脚本:
function outer
{
[cmdletbinding(supportsshouldprocess=$true)]
param($s)
process
{
$pscmdlet.shouldprocess("outer $s", "ShouldProcess") | out-null
"" | out-file "outer $s"
inner ImplicitPassthru
inner VerbosePassthru -Verbose:$Verbose
inner WhatifPassthru -WhatIf:$WhatIf
}
}
function inner
{
[cmdletbinding(supportsshouldprocess=$true)]
param($s)
process
{
$pscmdlet.shouldprocess("inner $s", "ShouldProcess") | out-null
"" | out-file "inner $s"
}
}
"`n** NORMAL **"
outer normal
"`n** VERBOSE **"
outer verbose -Verbose
"`n** WHATIF **"
outer whatif -WhatIf
输出:
** NORMAL **
VERBOSE: Performing operation "ShouldProcess" on Target "inner VerbosePassthru".
What if: Performing operation "ShouldProcess" on Target "inner WhatifPassthru".
What if: Performing operation "Output to File" on Target "inner WhatifPassthru".
** VERBOSE **
VERBOSE: Performing operation "ShouldProcess" on Target "outer verbose".
VERBOSE: Performing operation "ShouldProcess" on Target "inner VerbosePassthru".
What if: Performing operation "ShouldProcess" on Target "inner WhatifPassthru".
What if: Performing operation "Output to File" on Target "inner WhatifPassthru".
** WHATIF **
What if: Performing operation "ShouldProcess" on Target "outer whatif".
What if: Performing operation "Output to File" on Target "outer whatif".
What if: Performing operation "ShouldProcess" on Target "inner ImplicitPassthru".
What if: Performing operation "Output to File" on Target "inner ImplicitPassthru".
What if: Performing operation "ShouldProcess" on Target "inner VerbosePassthru".
What if: Performing operation "Output to File" on Target "inner VerbosePassthru".
What if: Performing operation "ShouldProcess" on Target "inner WhatifPassthru".
What if: Performing operation "Output to File" on Target "inner WhatifPassthru".
在我看来,这里有几个怪异之处:
N.B. :确认行为与WhatIf相同。为简洁起见,我省略了它。
在网络上搜索和连接,我几乎看不到与高级功能有关的ShouldProcess行为(赞成或反对)的深入讨论。最近的是a post from James O'Neill,建议在整个调用堆栈中传递$ psCmdlet的单个实例。但是,他这样做是为了解决一个完全不同的问题(避免使用多个-Confirm提示)。同时,当您坚持为每个函数提供标准的$ psCmdlet时,我看不到任何有关预期结果的文档……更少的设计模式,最佳实践等……
最佳答案
您实际上不能引用$ WhatIf或$ Verbose,因为它们是为您合成的,即这些变量在您的函数中不存在。如果用户指定了它们,则可以通过$ PSBoundParameters获得它们,但如果用户未指定,则显然它们将不在此哈希表中。
当您将值传递给交换机时,PowerShell将执行典型的强制过程,尝试将指定的值转换为 bool(boolean) 值。由于未定义$ whatif,因此将其评估为$ null,这将导致switch值设置为$ true。据推测,这是因为它看到该开关实际上是明确指定为无值的,这等效于仅指定-Whatif为无值。跟踪参数绑定(bind)时可以看到以下内容:
function Foo
{
[CmdletBinding(SupportsShouldProcess=1)]
param()
Process
{
$PSBoundParameters
}
}
Trace-Command -name ParameterBinding -expr {Foo -whatif:$xyzzy} -PSHost
DEBUG: BIND NAMED cmd line args [Foo]
DEBUG: BIND arg [] to parameter [WhatIf]
DEBUG: COERCE arg to [System.Management.Automation.SwitchParameter]
DEBUG: Arg is null or not present, type is SWITCHPARAMTER, value is true.
DEBUG: BIND arg [True] to param [WhatIf] SUCCESSFUL
DEBUG: BIND POSITIONAL cmd line args [Foo]
DEBUG: MANDATORY PARAMETER CHECK on cmdlet [Foo]
DEBUG: CALLING BeginProcessing
DEBUG: CALLING EndProcessing
$ WhatIfPreference和$ VerbosePreference可以根据是否使用-verbose或-whatif调用external来适当设置。我可以看到那些值传播到内部就好了。似乎$ pscmdlet.ShouldProcess存在PowerShell错误。在这种情况下,它似乎不符合$ VerbosePreference的值。您可以尝试将-Verbose传递给inner,如下所示:
inner VerbosePassthru -Verbose:($VerbosePreference -eq 'Continue')
另一个选择是使用Get-Variable -Scope,如下所示:
function Outer
{
[CmdletBinding(SupportsShouldProcess=1)]
param()
Process
{
$pscmdlet.ShouldProcess("Outer process", '') > $null
inner
#inner -Verbose:($VerbosePreference -eq 'Continue')
}
}
function Inner
{
[CmdletBinding(SupportsShouldProcess=1)]
param()
Process
{
$pscmdlet = (Get-Variable -Scope 1 -Name PSCmdlet).Value
$pscmdlet.ShouldProcess("Inner process", '') > $null
"Inner $VerbosePreference"
}
}
Outer -Verbose
我不确定我喜欢这样,因为这意味着您知道外层比内层高1级。您可以“遍历”作用域堆栈,以在堆栈中查找下一个PSCmdlet变量。这有效地消除了必须传递PSCmdlet(这是很麻烦的),但这仍然是一个hack。您应该考虑为此在MS Connect上提交错误。