_-WhatIf
_および_-Confirm
_パラメーターをサポートするPowerShellスクリプトコマンドレットがあります。
これは、変更を実行する前に$PSCmdlet.ShouldProcess()
メソッドを呼び出すことによって行われます。
これは期待どおりに機能します。
私が抱えている問題は、私のコマンドレットが他のコマンドレットを呼び出すことによって実装され、_-WhatIf
_または_-Confirm
_パラメーターが私が呼び出すコマンドレットに渡されないことです。
_-WhatIf
_と_-Confirm
_の値をコマンドレットから呼び出すコマンドレットに渡すにはどうすればよいですか?
たとえば、コマンドレットが_Stop-CompanyXyzServices
_であり、_Stop-Service
_を使用してアクションを実装している場合です。
_-WhatIf
_が_Stop-CompanyXyzServices
_に渡された場合、Stop-Serviceにも渡されます。
これは可能ですか?
いくつかグーグルした後、呼び出されたコマンドに共通のパラメーターを渡すための良い解決策を思いつきました。 @ 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などのパラメーターを渡すと、呼び出し元の意図が尊重されます。
パラメータを明示的に渡す
_-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
_スイッチを設定するようにコードを更新しました。
これは、@ 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
のためだけに同じコマンドセットを再度呼び出すことはありません。これが誰かを助けることを願っています。
@ 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)
}
}
}