13

テスト スクリプト:

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

私の目には、ここにいくつかの奇妙な点があります。

  • -WhatIf:$foo を指定すると、$foo が何であれ、呼び出し先 (およびその呼び出し先) で常に$WhatIf がオンになります。
  • -WhatIf を "実際に" (既存の変数に制約せずに) 指定すると、呼び出し先に暗黙的に伝達されます。パススルーやスプラッティングは必要ありません。
  • -WhatIf とは異なり、明示的な -Verbose は呼び出し先に暗黙的にカスケードしません。
  • -Verbose:$foo を手動でパススルーしようとすると、動作が -WhatIf:$foo に似ていることがわかります。ただし、$psCmdlet.ShouldProcess() を手動でテストするスクリプトにのみ影響します。組み込みのコマンドレットは影響を受けません。

注意: Confirm の動作は WhatIf と同じです。簡潔にするために省略しました。

Web と Connect を検索すると、高度な機能に関連する ShouldProcess の動作 (賛否両論) についての詳細な議論はほとんど見当たりません。最も近いものは、コール スタック全体で $psCmdlet の 1 つのインスタンスを渡すことを推奨するJames O'Neill の投稿です。ただし、彼はまったく別の問題を回避するためにそうします (複数の -Confirm プロンプトを回避します)。一方、各関数に提供されている標準の $psCmdlet に固執すると、何を期待するかについてのドキュメントが表示されません...デザインパターンやベストプラクティスなどははるかに少ないです...

4

2 に答える 2

12

$WhatIfまたは$Verboseは合成されているため、実際には参照できません。つまり、これらの変数は関数に存在しません。ユーザーがそれらを指定した場合、$ PSBoundParametersを介してそれらを取得できますが、ユーザーが指定しなかった場合、明らかにそれらはこのハッシュテーブルに含まれません。

値をスイッチに渡すと、PowerShellは通常の強制プロセスを実行して、指定された値をブール値に変換しようとします。$ whatifが定義されていないため、この評価は$ nullになり、スイッチ値が$trueに設定されます。これはおそらく、スイッチが実質的に値なしで明示的に指定されていることを確認しているためです。これは、値なしで-Whatifを指定するのと同じです。パラメータバインディングをトレースすると、次のことがわかります。

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は、outerが-verboseと-whatifのどちらで呼び出されたかに基づいて、outerで適切に設定されます。これらの値が内部にうまく伝播していることがわかります。$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(グロス)を渡す必要がなくなりますが、それでもハックです。これについては、MSConnectにバグを報告することを検討する必要があります。

于 2010-02-22T21:00:15.667 に答える