web-dev-qa-db-ja.com

他のコマンドレットを呼び出すコマンドレットで、PowerShellの-WhatIfおよび-Confirmパラメーターをどのようにサポートしますか?

_-WhatIf_および_-Confirm_パラメーターをサポートするPowerShellスクリプトコマンドレットがあります。

これは、変更を実行する前に$PSCmdlet.ShouldProcess()メソッドを呼び出すことによって行われます。
これは期待どおりに機能します。

私が抱えている問題は、私のコマンドレットが他のコマンドレットを呼び出すことによって実装され、_-WhatIf_または_-Confirm_パラメーターが私が呼び出すコマンドレットに渡されないことです。

_-WhatIf_と_-Confirm_の値をコマンドレットから呼び出すコマンドレットに渡すにはどうすればよいですか?

たとえば、コマンドレットが_Stop-CompanyXyzServices_であり、_Stop-Service_を使用してアクションを実装している場合です。

_-WhatIf_が_Stop-CompanyXyzServices_に渡された場合、Stop-Serviceにも渡されます。

これは可能ですか?

22
Dan Finucane

いくつかグーグルした後、呼び出されたコマンドに共通のパラメーターを渡すための良い解決策を思いつきました。 @ splatting演算子を使用して、コマンドに渡されたすべてのパラメーターを渡すことができます。たとえば、

Start-Service -Name ServiceAbc @PSBoundParameters

スクリプトの本体にあるPowerShellは、スクリプトに渡されたすべてのパラメーターをStart-Serviceコマンドに渡します。唯一の問題は、スクリプトに-Nameパラメーターが含まれている場合、それも渡され、PowerShellが-Nameパラメーターを2回含めたと文句を言うことです。次の関数を作成して、すべての共通パラメーターを新しい辞書にコピーしてから、それをスプラットします。

function Select-BoundCommonParameters
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        $BoundParameters
    )
    begin
    {
        $boundCommonParameters = New-Object -TypeName 'System.Collections.Generic.Dictionary[string, [Object]]'
    }
    process
    {
        $BoundParameters.GetEnumerator() |
            Where-Object { $_.Key -match 'Debug|ErrorAction|ErrorVariable|WarningAction|WarningVariable|Verbose' } |
            ForEach-Object { $boundCommonParameters.Add($_.Key, $_.Value) }

        $boundCommonParameters
    }
}

最終的には、スクリプトで呼び出されたコマンドに-Verboseなどのパラメーターを渡すと、呼び出し元の意図が尊重されます。

7
Dan Finucane

パラメータを明示的に渡す

_-WhatIf_および_-Confirm_パラメーターを_$WhatIfPreference_および_$ConfirmPreference_変数と一緒に渡すことができます。次の例では、 パラメータスプラッティング でこれを実現しています。

_if($ConfirmPreference -eq 'Low') {$conf = @{Confirm = $true}}

StopService MyService -WhatIf:([bool]$WhatIfPreference.IsPresent) @conf
_

_$WhatIfPreference.IsPresent_スイッチが包含関数で使用されている場合、_-WhatIf_はTrueになります。包含関数の_-Confirm_スイッチを使用すると、一時的に_$ConfirmPreference_がlowに設定されます。

パラメータを暗黙的に渡す

_-Confirm_および_-WhatIf_は一時的に_$ConfirmPreference_および_$WhatIfPreference_変数を自動的に設定するので、それらを渡す必要さえありますか?

例を考えてみましょう。

_function ShouldTestCallee {
    [cmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='Medium')] 
    param($test)

    $PSCmdlet.ShouldProcess($env:COMPUTERNAME,"Confirm?")
}


function ShouldTestCaller {
    [cmdletBinding(SupportsShouldProcess=$true)]
    param($test)

    ShouldTestCallee
}

$ConfirmPreference = 'High'
ShouldTestCaller
ShouldTestCaller -Confirm
_

ShouldTestCallerは、ShouldProcess()からTrueになります。

_ShouldTestCaller -Confirm_は、スイッチを渡さなかったにもかかわらず、確認プロンプトを表示します。

編集

@manojldsの回答により、私のソリューションは常に_$ConfirmPreference_を「低」または「高」に設定していることに気付きました。確認設定が「低」の場合にのみ_-Confirm_スイッチを設定するようにコードを更新しました。

16
Rynant

これは、@ Rynantと@ShayLevyの回答に基づく完全なソリューションです。

function Stop-CompanyXyzServices
{
    [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='Medium')]

    Param(
        [Parameter(
            Position=0,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true
        )]      
        [string]$Name
    )

    process
    {
        if($PSCmdlet.ShouldProcess($env:COMPUTERNAME,"Stop XYZ services '$Name'")){  
            ActualCmdletProcess
        }
        if([bool]$WhatIfPreference.IsPresent){
            ActualCmdletProcess
        }
    }
}

function ActualCmdletProcess{
# add here the actual logic of your cmdlet, and any call to other cmdlets
Stop-Service $name -WhatIf:([bool]$WhatIfPreference.IsPresent) -Confirm:("Low","Medium" -contains $ConfirmPreference)
}

Whatifを個々のコマンドレットに渡すことができるように、-WhatIfも個別に渡されるかどうかを確認する必要があります。 ActualCmdletProcessは基本的にリファクタリングであるため、WhatIfのためだけに同じコマンドセットを再度呼び出すことはありません。これが誰かを助けることを願っています。

3
manojlds

@ manojldsコメントごとに更新

$ WhatIfと$ Confirmをブール値にキャストし、基になるコマンドレットに値を渡します。

function Stop-CompanyXyzServices
{
    [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='High')]

    Param(
        [Parameter(
            Position=0,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true
        )]      
        [string]$Name
    )


    process
    {
        if($PSCmdlet.ShouldProcess($env:COMPUTERNAME,"Stop service '$Name'"))
        {                   
            Stop-Service $name -WhatIf:([bool]$WhatIf) -Confirm:([bool]$confirm)
        }                       
    }
}
0
Shay Levy