如何支持 PowerShell 的 -WhatIf & - 确认调用其他 Cmdlet 的 Cmdlet 中的参数吗?

发布于 2024-12-01 06:11:33 字数 569 浏览 1 评论 0原文

我有一个支持 -WhatIf 的 PowerShell 脚本 cmdlet -确认参数。

它通过在执行更改之前调用 $PSCmdlet.ShouldProcess() 方法来实现此目的。
这按预期工作。

我遇到的问题是,我的 Cmdlet 是通过调用其他 Cmdlet 来实现的,并且 -WhatIf-Confirm 参数不会传递到我调用的 Cmdlet。

如何将 -WhatIf-Confirm 的值传递到我从 Cmdlet 调用的 Cmdlet?

例如,如果我的 Cmdlet 是 Stop-CompanyXyzServices 并且它使用 Stop-Service 来实现其操作。

如果 -WhatIf 传递给 Stop-CompanyXyzServices 我希望它也传递给 Stop-Service。

这可能吗?

I have a PowerShell script cmdlet that supports the -WhatIf & -Confirm parameters.

It does this by calling the $PSCmdlet.ShouldProcess() method before performing the change.
This works as expected.

The problem I have is that my Cmdlet is implemented by calling other Cmdlets and the -WhatIf or -Confirm parameters are not passed along to the Cmdlets I invoke.

How can I pass along the values of -WhatIf and -Confirm to the Cmdlets I call from my Cmdlet?

For example, if my Cmdlet is Stop-CompanyXyzServices and it uses Stop-Service to implement its action.

If -WhatIf is passed to Stop-CompanyXyzServices I want it to also be passed to Stop-Service.

Is this possible?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

删除→记忆 2024-12-08 06:11:33

显式传递参数

您可以使用 $WhatIfPreference$ 传递 -WhatIf-Confirm 参数确认首选项变量。以下示例通过参数splatting< /a>:

if($ConfirmPreference -eq 'Low') {$conf = @{Confirm = $true}}

StopService MyService -WhatIf:([bool]$WhatIfPreference.IsPresent) @conf

如果在包含函数上使用 -WhatIf 开关,$WhatIfPreference.IsPresent 将为 True。使用包含函数上的 -Confirm 开关暂时将 $ConfirmPreference 设置为 low

隐式传递参数

由于-Confirm-WhatIf临时设置了$ConfirmPreference$WhatIfPreference< /code> 自动变量,是否有必要传递它们?

考虑这个例子:

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

ShouldTestCallerShouldProcess() 得到 True

即使 ShouldTestCaller -Confirm 产生确认提示我没有通过开关。

编辑

@manojlds 答案让我意识到我的解决方案始终将 $ConfirmPreference 设置为“低”或“高”。我已更新我的代码,仅在确认首选项为“低”时设置 -Confirm 开关。

Passing parameters explicitly

You can pass the -WhatIf and -Confirm parameters with the $WhatIfPreference and $ConfirmPreference variables. The following example achieves this with parameter splatting:

if($ConfirmPreference -eq 'Low') {$conf = @{Confirm = $true}}

StopService MyService -WhatIf:([bool]$WhatIfPreference.IsPresent) @conf

$WhatIfPreference.IsPresent will be True if the -WhatIf switch is used on the containing function. Using the -Confirm switch on the containing function temporarily sets $ConfirmPreference to low.

Passing parameters implicitly

Since the -Confirm and -WhatIf temporarily set the $ConfirmPreference and $WhatIfPreference variables automatically, is it even necessary to pass them?

Consider the example:

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 results in True from ShouldProcess()

ShouldTestCaller -Confirm results in an confirm prompt even though I didn't pass the switch.

Edit

@manojlds answer made me realize that my solution was always setting $ConfirmPreference to 'Low' or 'High'. I have updated my code to only set the -Confirm switch if the confirm preference is 'Low'.

柏林苍穹下 2024-12-08 06:11:33

经过一番谷歌搜索后,我想出了一个很好的解决方案,用于将通用参数传递给被调用的命令。您可以使用 @ 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 等参数传递给脚本中调用的命令,并且它们尊重调用者的意图。

After some googling I came up with a good solution for passing common parameters along to called commands. You can use the @ splatting operator to pass along all the parameters that were passed to your command. For example, if

Start-Service -Name ServiceAbc @PSBoundParameters

is in the body of your script powershell will pass all the parameters that were passed to your script to the Start-Service command. The only problem is that if your script contains say a -Name parameter it will be passed too and PowerShell will complain that you included the -Name parameter twice. I wrote the following function to copy all the common parameters to a new dictionary and then I splat that.

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
    }
}

The end result is you pass parameters like -Verbose along to the commands called in your script and they honor the callers intention.

帥小哥 2024-12-08 06:11:33

这是基于 @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 传递到各个 cmdlet。 ActualCmdletProcess 基本上是一种重构,这样您就不会仅为 WhatIf 再次调用同一组命令。希望这对某人有帮助。

Here is a complete solution based on @Rynant and @Shay Levy's answers:

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)
}

We have to see if -WhatIf is passed separately as well so that the whatif can be passed on to the individual cmdlets. ActualCmdletProcess is basically a refactoring so that you don't call the same set of commands again just for the WhatIf. Hope this helps someone.

却一份温柔 2024-12-08 06:11:33

根据 @manojlds 评论进行更新

将 $WhatIf 和 $Confirm 转换为布尔值并将值传递给底层 cmdlet:

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)
        }                       
    }
}

Updated per @manojlds comment

Cast $WhatIf and $Confirm to Boolean and pass the values to the the underlying cmdlet:

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)
        }                       
    }
}
初熏 2024-12-08 06:11:33

为了避免您因这个问题和此处的答案而困扰几个小时,我建议您改为阅读这篇文章:

https://powershellexplained.com/2020-03-15-Powershell-shouldprocess-whatif-confirm-shouldcontinue-everything/#suppressing-nested-confirm-prompts

这里提供的答案对很多人来说并不适用我发现人们在不了解基本原理的情况下实施这里的答案是危险的。

以下是如何破解它以跨脚本模块工作:
输入图片此处描述

Just so you wont get run around the block for hours by this question and the answers here, I would suggest that you read this article instead:

https://powershellexplained.com/2020-03-15-Powershell-shouldprocess-whatif-confirm-shouldcontinue-everything/#suppressing-nested-confirm-prompts

The answers presented here does not work for many cases and I see a danger in people implementing the answers here, without understanding the fundamentals.

Here is how a hacked it to work across scriptmodules:
enter image description here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文