$PSCmdlet.ShouldProcess 状态在调用之间存储在哪里(以及如何重置)?

jim*_*gee 3 powershell

考虑以下人为的例子:

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"
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

...及其潜在输出之一:

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
Run Code Online (Sandbox Code Playgroud)

ShouldContinue循环(第三个for循环)的情况下,我可以看到带有两个 by-reference 布尔参数的重载负责将最终用户选择Yes to All还是No to All 存储到这两个布尔值中。

但是,在两个ShouldProcess块(前两个for循环)的情况下,这种状态是如何保持的?

特别是,在前两个ShouldProcess块之间,我如何检查是否指定了Yes to AllNo to All和/或我需要重置或清除什么才能使第二个ShouldProcess块再次要求确认?

(偏袒ShouldContinueShouldProcess选择细粒度控制,但它似乎失去了本地/内置支持[CmdletBinding(SupportsShouldProcess=$true)]

bri*_*ist 5

首先,我将解决$PSCmdlet.ShouldContinue. 无论确认偏好如何,这基本上是一种自行提示的方法。

$PSCmdlet.ShouldProcess另一方面,并​​不总是提示。它考虑了ConfirmImpact(您设置为High)和$ConfirmPreference默认为的自动变量High。有效值为NoneLowMedium、 和High用于指示更改有多大影响,因此当$ConfirmPreference的值等于或小于命令的ConfirmImpact值时,ShouldProcess将提示。

我知道这不是您的直接问题,但背景对于回答您应该做什么很重要。

直接问题:“答案存储在哪里?” 有一个无聊的答案:它存储在定义ShouldProcess方法的类的内部变量中

所以,不,不幸的是,你不能自己解决。

但这让我们回到了.ShouldContinue,它可以为您获取这些引用并存储这些值,因此当您需要这些值并希望能够使用它们做出决定时,您应该使用.ShouldContinue.

但是,你真的应该同时使用两者。因为他们做不同的事情。

.ShouldProcess不仅负责确认提示,还负责处理-WhatIf/ $WhatIfPreference;当你说你的命令时,SupportsShouldProcess你也说它支持-WhatIf. 如果您不使用.ShouldProcess,您将遇到以下情况:命令看似安全,但实际上还是要采取行动。

因此,类似这样的模式将涵盖您的基础:

if ($PSCmdet.ShouldProcess('thing', 'do')) {
    if ($PSCmdlet.ShouldContinue('prompt')) {
        # do it
    }
}
Run Code Online (Sandbox Code Playgroud)

这个问题可以追溯到您确认的影响和偏好。如果这些对齐,或者如果用户使用 调用您的命令-Confirm,您将提示两次:一次在 中.ShouldProcess,然后在.ShouldContinue.

不幸的是,这种事情很糟糕。

我写了一个似乎可以解决这个问题的东西。它首先基于一个函数,该函数允许您在确认的情况下运行任意脚本块,以便您.ShouldProcess在抑制其提示的同时仍然可以运行。

然后它还会尝试计算是否需要提示,然后有选择地调用.ShouldContinue. 我没有演示存储或重置 yesToAll 和 noToAll 变量,因为您已经知道如何做到这一点。

这主要是为了演示一种模式,该模式可用于遵守标准确认提示语义,具有可发现性,支持-Confirm参数$ConfirmPreference, and ConfirmImpact,同时保持对-Verboseand 的支持-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
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

调用:

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
Run Code Online (Sandbox Code Playgroud)

依此类推,$ConfirmPreference命令的ConfirmImpact.

令人讨厌的一件事是我标记为硬编码的值:它必须与您为该命令设置的确认影响相匹配。

事实证明,以编程方式获得该值有点麻烦,但也许您可以以某种方式解决这个问题。