Powershell脚本:函数调用嵌套时推荐使用ShouldProcess的方法? [英] Powershell scripting: recommended way to implement ShouldProcess when function calls are nested?

查看:519
本文介绍了Powershell脚本:函数调用嵌套时推荐使用ShouldProcess的方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

测试脚本:

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".

对我来说,这里有几个怪物:

To my eye there are several oddities here:


  • 指定-WhatIf:$ foo将始终打开被叫方(及其被调用者)中的$ WhatIf,无论$ foo是什么。

  • 当您指定-WhatIf为真实(不将其约束到现有变量)时,它会隐式传播到被调用者。不需要passthru或splatting。

  • 与-WhatIf不同,显式-Verbose不会隐式级联到被调用者。

  • 当您尝试手动passthru - 详细说明:$ foo,你确实看到行为类似于-WhatIf:$ foo。但它只影响手动测试$ psCmdlet.ShouldProcess()的脚本 - 内置cmdlet不受影响。

  • Specifying -WhatIf:$foo will always turn on $WhatIf in the callee (and its callees), no matter what $foo is.
  • When you do specify -WhatIf "for real" (without constraining it to an existing variable), it propagates to callees implicitly. No need for passthru or splatting.
  • Unlike -WhatIf, explicit -Verbose does not cascade to callees implicitly.
  • When you try to manually passthru -Verbose:$foo, you do see behavior is similar to -WhatIf:$foo. But it only affects scripts that manually test $psCmdlet.ShouldProcess() -- built in cmdlets aren't affected.

NB :确认的行为与WhatIf相同。我简单的省略了它。

N.B.: Confirm behaves identical to WhatIf. I omitted it for brevity.

搜索网络和连接,几乎没有任何关于ShouldProcess行为(pro或con)的深入讨论先进的功能。最近的事情是来自詹姆斯的一篇文章O'Neill 建议在调用堆栈中传递$ psCmdlet的单个实例。但是,他这样做是为了解决一个完全不同的问题(避免多个 - 确认提示)。同时,当你坚持使用每个函数提供的标准$ psCmdlet时,我没有看到什么期望的文档...更少的设计模式,最佳实践等...

Searching the web and Connect, I see hardly any in-depth discussion of ShouldProcess behavior (pro or con) as pertains to advanced functions. Closest thing is a post from James O'Neill that recommends passing a single instance of $psCmdlet throughout the call stack. However, he does so to workaround an entirely different problem (avoiding multiple -Confirm prompts). Meanwhile, when you stick with the standard $psCmdlet provided to each function, I see no docs on what to expect...much less design patterns, best practices, etc...

推荐答案

你不能真正地引用$ WhatIf或$ Verbose,因为这些是为您合成的,即这些变量不存在于您的函数中。如果用户指定它们,那么您可以通过$ PSBoundParameters获取它们,但是如果用户没有明确指定它们将不在此散列表中。

You can't really refer to $WhatIf or $Verbose since these are synthesized for you i.e. these variables don't exist in your function. If the user specifies them then you can get at them via $PSBoundParameters but if the user didn't specify then obviously they won't be in this hashtable.

当您将值传递给交换机时,PowerShell将执行典型的强制过程以尝试将指定的值转换为bool。由于$ whatif没有被定义,所以这将导致$ null,导致switch值设置为$ true。这可能是因为它看到交换机被明确指定,没有任何值只是指定 - 没有值的值。您可以在跟踪参数绑定时看到这一点:

When you pass a value to a switch PowerShell will do the typical coercion process to attempt to convert the specified value to a bool. Since $whatif isn't defined this evals to a $null which results in the switch value being set to $true. This is presumably because it sees the switch is explicitly specified with effectively no value which is the equivalent of just specifying -Whatif with no value. You can see this when you trace the parameter binding:

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调用外部。我可以看到,这些价值观传播到内心就好了。似乎有一个PowerShell错误与$ pscmdlet.ShouldProcess。在这种情况下,似乎不符合$ VerbosePreference的价值。你可以尝试通过-Verbose到内部,如下所示:

The $WhatIfPreference and $VerbosePreference gets set appropriately in outer based on whether outer was called with -verbose or -whatif. I can see that those values propagate to inner just fine. It would seem that there is a PowerShell bug with $pscmdlet.ShouldProcess. It doesn't seem to be honoring the value of $VerbosePreference in this case. You could try passing through -Verbose to inner like so:

inner VerbosePassthru -Verbose:($VerbosePreference -eq 'Continue')

另一个选项是使用Get-Variable -Scope:

Another option is to use Get-Variable -Scope like so:

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(这是粗体),但它仍然是一个黑客。您应该考虑在MS Connect上提交有关此问题的错误。

I'm not sure I like this because it implies that you know outer is 1 level above inner. You could "walk" the scope stack looking for the next PSCmdlet variable up the stack. This effectively gets rid of having to pass in PSCmdlet (which is gross) but it's still a hack. You should consider filing a bug on MS Connect about this.

这篇关于Powershell脚本:函数调用嵌套时推荐使用ShouldProcess的方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆