Где хранится состояние $ PSCmdlet.ShouldProcess между вызовами (и как его можно сбросить)? - PullRequest
3 голосов
/ 07 июня 2019

Рассмотрим следующий надуманный пример:

function Test-ProcessContinue {
    [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='High')]
    Param()

    for ($i = 1; $i -le 3; $i++) {
        if ($PSCmdlet.ShouldProcess("$i", "Process")) {
            Write-Output "Processing $i"
        }
        else {
            Write-Verbose "No chosen"
        }
    }

    for ($i = 1; $i -le 3; $i++) {
        if ($PSCmdlet.ShouldProcess("$i", "Process")) {
            Write-Output "Processing $i"
        }
        else {
            Write-Verbose "No chosen"
        }
    }

    $yta = $false; $nta = $false
    for ($i = 1; $i -le 3; $i++) {
        if ($PSCmdlet.ShouldContinue("$i", "Continue", [ref]$yta, [ref]$nta) -or $yta) {
            Write-Output "Continuing with $i"
        }
        elseif ($nta) {
            Write-Verbose "No to all chosen"
            break
        }
        else {
            Write-Verbose "No chosen"
        }
    }
}

... и один из его потенциальных выходов:

PS C:\> Test-ProcessContinue -Verbose

Confirm
Are you sure you want to perform this action?
Performing the operation "Process" on target "1".
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): a
Processing 1
Processing 2
Processing 3
Processing 1
Processing 2
Processing 3

Continue
1
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): a
Continuing with 1
Continuing with 2
Continuing with 3

В случае петли ShouldContinue (третий * 1008)* loop), я вижу, что перегрузка с двумя булевыми параметрами по ссылкам отвечает за сохранение того, выбрал ли конечный пользователь Да для всех или Нет для всех для этих двухbooleans.

Однако, как это состояние сохраняется в случае двух ShouldProcess блоков (первые два цикла for)?

В частности, между первыми двумя ShouldProcess блоков, как я могу проверить, были ли указаны Да для всех или Нет для всех и / или что мне нужно сбросить или очистить, чтобы сделать второй ShouldProcessблок запрашивает подтверждение еще раз?

(Пользуясь ShouldContinue более ShouldProcess, это опция для детального контроля, но, похоже, она теряет встроенную / встроенную поддержку [CmdletBinding(SupportsShouldProcess=$true)]

1 Ответ

4 голосов
/ 07 июня 2019

Сначала я займусь $PSCmdlet.ShouldContinue. По сути, это способ запроса самостоятельно независимо от настроек подтверждения.

$PSCmdlet.ShouldProcess с другой стороны, не всегда подсказывает. Он учитывает ConfirmImpact (который вы установили на High) и автоматическую переменную $ConfirmPreference, которая по умолчанию High. Допустимые значения: None, Low, Medium и High и предназначены для указания степени воздействия изменения, поэтому, когда значение $ConfirmPreference равно или меньше значения команды ConfirmImpact значение, тогда ShouldProcess подскажет.

Я знаю, что это не ваш прямой вопрос, но фон важен для ответа на то, что вы должны делать.

Прямой вопрос: "где хранится ответ?" имеет скучный ответ: он хранится во внутренней переменной в классе, который определяет ShouldProcess метод .

Так что, нет, вы, к сожалению, не можете этого понять.

Но это возвращает нас к .ShouldContinue, который может взять эти ссылки и сохранить эти значения для вас, поэтому, когда вы хотите получить значения и хотите иметь возможность принимать решения с ними, вы должны использовать .ShouldContinue.

Но вы действительно должны использовать оба. Потому что они делают разные вещи.

.ShouldProcess не только отвечает за запросы подтверждения, но и отвечает за обработку -WhatIf / $WhatIfPreference; когда вы произносите команду SupportsShouldProcess, вы также говорите, что она поддерживает -WhatIf. Если вы не используете .ShouldProcess, вы попадете в ситуацию с командами, которые кажутся безопасными, но на самом деле принимают меры в любом случае.

Таким образом, шаблон чего-то подобного будет охватывать ваши базы:

if ($PSCmdet.ShouldProcess('thing', 'do')) {
    if ($PSCmdlet.ShouldContinue('prompt')) {
        # do it
    }
}

Проблема с этим восходит к вашему подтверждению влияния и предпочтений. Если они совпадают, или если пользователь вызвал вашу команду с -Confirm, вам будет предложено дважды: один раз в .ShouldProcess и затем снова в .ShouldContinue.

К сожалению, это отстой.

Я написал вещь, которая, кажется, работает вокруг этого. Сначала он основан на функции, которая позволяет вам запускать произвольный блок сценариев с подтверждением, так что вы все равно можете запустить .ShouldProcess, подавляя его приглашение.

Затем он также пытается вычислить, нужна ли подсказка или нет, и затем выборочно вызвать .ShouldContinue. Я не демонстрировал сохранение или сброс настроек переменных yesToAll и noToAll, потому что вы уже знаете, как это сделать.

Это в основном для демонстрации шаблона, который можно использовать для соответствия стандартной семантике приглашения подтверждения, с возможностью обнаружения, поддержкой параметров -Confirm, $ConfirmPreference и ConfirmImpact, при сохранении поддержки -Verbose и -WhatIf.

function Test-Should {
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    param()

    Begin {
        $local:ShouldConfirm = $ConfirmPreference -ne [System.Management.Automation.ConfirmImpact]::None -and 
                               $ConfirmPreference -le [System.Management.Automation.ConfirmImpact]::High # hard coded :(

        function Invoke-CommandWithConfirmation {
            [CmdletBinding(SupportsShouldProcess)]
            param(
                [Parameter(Mandatory)]
                [ScriptBlock]
                $ScriptBlock
            )

            Invoke-Command -NoNewScope -ScriptBlock $ScriptBlock
        }
    }

    Process {
        if (Invoke-CommandWithConfirmation -ScriptBlock { $PSCmdlet.ShouldProcess('target', 'action') } -Confirm:$false ) {
            if (-not $local:ShouldConfirm -or $PSCmdlet.ShouldContinue('query', 'caption')) {
                'Hi' | Write-Host
                'Hello' | Write-Verbose
            }
        }
    }
}

Вызовы:

Test-Should
Test-Should -Confirm
Test-Should -Confirm:$false
Test-Should -Verbose
Test-Should -Verbose -WhatIf
Test-Should -WhatIf -Confirm
Test-Should -WhatIf -Confirm:$false

И так далее, с разными значениями $ConfirmPreference и разными значениями для команды ConfirmImpact.

Единственное, что раздражает, - это значение, которое я пометил как жестко запрограммированное: оно должно совпадать с тем, которое вы указали в качестве подтверждения для этой команды.

Оказывается, это задница в заднице - программно получить это значение , но, возможно, вы могли бы каким-то образом это обработать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...