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

时间:2011-08-24 18:15:53

标签: powershell powershell-v2.0 cmdlets

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

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

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

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

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

如果-WhatIf传递给Stop-CompanyXyzServices我希望它也传递给停止服务。

这可能吗?

4 个答案:

答案 0 :(得分:14)

明确传递参数

您可以使用-WhatIf-Confirm变量传递$WhatIfPreference$ConfirmPreference参数。以下示例使用parameter 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也会出现确认提示。

修改

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

答案 1 :(得分:6)

经过一些谷歌搜索后,我想出了一个很好的解决方案,用于将常用参数传递给被调用的命令。您可以使用@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 :(得分:2)

这是一个完整的解决方案,基于@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再次调用同一组命令。希望这有助于某人。

答案 3 :(得分:0)

根据@manojlds评论更新

将$ WhatIf和$ Confirm转换为Boolean并将值传递给基础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)
        }                       
    }
}