Как вы поддерживаете параметры PowerShell -WhatIf & -Confirm в командлете, который вызывает другие командлеты?
У меня есть командлет PowerShell script, который поддерживает параметры -WhatIf
и -Confirm
.
Он делает это, вызывая метод $PSCmdlet.ShouldProcess()
перед выполнением изменения.
Это работает так, как ожидалось.
Проблема заключается в том, что мой командлет реализован путем вызова других командлетов, а параметры -WhatIf
или -Confirm
не передаются вместе с командлетами, которые я вызываю.
Как передать значения -WhatIf
и -Confirm
в командлеты, которые я вызываю из своего Cmdlet?
Например, если мой Cmdlet равен Stop-CompanyXyzServices
, и он использует Stop-Service
для реализации своего действия.
Если -WhatIf
передано в Stop-CompanyXyzServices
, я хочу, чтобы он также передавался в Stop-Service.
Возможно ли это?
Ответы
Ответ 1
После некоторого googling я придумал хорошее решение для передачи общих параметров по вызываемым командам. Вы можете использовать оператор @splatting для передачи всех параметров, которые были переданы вашей команде. Например, если
Start-Service -Name ServiceAbc @PSBoundParameters
находится в теле вашей script powershell передаст все параметры, которые были переданы вашему script команде Start-Service. Единственная проблема заключается в том, что если ваш script содержит параметр -Name, он также будет передан, а PowerShell будет жаловаться на то, что вы дважды включили параметр -Name. Я написал следующую функцию, чтобы скопировать все общие параметры в новый словарь, а затем я разделил это.
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 вместе с командами, названными в вашем script, и они выполняют намерение вызывающих абонентов.
Ответ 2
Передача параметров явно
Вы можете передать параметры -WhatIf
и -Confirm
с помощью переменных $WhatIfPreference
и $ConfirmPreference
. В следующем примере это достигается с помощью параметрирования splatting:
if($ConfirmPreference -eq 'Low') {$conf = @{Confirm = $true}}
StopService MyService -WhatIf:([bool]$WhatIfPreference.IsPresent) @conf
$WhatIfPreference.IsPresent
будет True
, если переключатель -WhatIf
используется в содержащей функции. Использование переключателя -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
выводит True
из ShouldProcess()
ShouldTestCaller -Confirm
выводит запрос подтверждения, даже если я не прошел этот переключатель.
Edit
Ответ @manojlds заставил меня понять, что мое решение всегда устанавливало $ConfirmPreference
на "Низкий" или "Высокий". Я обновил свой код, чтобы установить только переключатель -Confirm
, если для параметра подтверждения выбрано значение "Низкий".
Ответ 3
Вот полное решение на основе ответов @Rynant и @Shay Levy:
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
. Надеюсь, это поможет кому-то.
Ответ 4
Обновлено за комментарий @manojlds
Ввести $WhatIf и $Подтвердить значение Boolean и передать значения командному командлу:
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)
}
}
}