Как вы поддерживаете параметры PowerShell -WhatIf & -Confirm в командлете, который вызывает другие командлеты? - PullRequest
21 голосов
/ 24 августа 2011

У меня есть командлет сценария PowerShell, который поддерживает параметры -WhatIf & -Confirm.

Это делается путем вызова метода $PSCmdlet.ShouldProcess() перед выполнением изменения.
Это работает как ожидалось.

Проблема, с которой я столкнулся, заключается в том, что мой командлет реализован путем вызова других командлетов, а параметры -WhatIf или -Confirm не передаются вызываемым командлетам.

Как я могу передать значения -WhatIf и -Confirm командлетам, которые я вызываю из моего командлета?

Например, если мой командлет Stop-CompanyXyzServices и он использует Stop-Service для реализации своего действия.

Если -WhatIf передается Stop-CompanyXyzServices, я хочу, чтобы он также передавался в Stop-Service.

Возможно ли это?

Ответы [ 4 ]

14 голосов
/ 24 августа 2011

Передача параметров в явном виде

Параметры -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 from ShouldProcess()

ShouldTestCaller -Confirm выдает запрос на подтверждение, хотя я не прошел переключатель.

Редактировать

@ manojlds ответ заставил меня понять, что мое решение всегда устанавливало $ConfirmPreference на «Низкий» или «Высокий».Я обновил свой код, чтобы установить переключатель -Confirm только в том случае, если для подтверждения выбрано «Низкий».

6 голосов
/ 03 ноября 2011

После некоторого поиска в Google я нашел хорошее решение для передачи общих параметров вызываемым командам.Вы можете использовать оператор @ splatting для передачи всех параметров, которые были переданы вашей команде.Например, если

Start-Service -Name ServiceAbc @ PSBoundParameters

в теле вашего сценария powershell передаст все параметры, которые были переданы вашему сценарию, команде Start-Service.Единственная проблема заключается в том, что если ваш скрипт содержит, скажем, параметр -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, командам, вызываемым в вашем скрипте, и онисоблюдать намерение звонящих.

2 голосов
/ 24 августа 2011

Вот полное решение, основанное на ответах @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. Надеюсь, это кому-нибудь поможет.

0 голосов
/ 25 августа 2011

Обновлено для комментария @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)
        }                       
    }
}
...