O script do Powershell:forma recomendada para implementar ShouldProcess quando chamadas de função são aninhados?

StackOverflow https://stackoverflow.com/questions/2278876

Pergunta

Script de teste:

function outer
{
    [cmdletbinding(supportsshouldprocess=$true)]
    param($s)

    process
    {        
        $pscmdlet.shouldprocess("outer $s", "ShouldProcess") | out-null
        "" | out-file "outer $s"

        inner ImplicitPassthru
        inner VerbosePassthru -Verbose:$Verbose 
        inner WhatifPassthru -WhatIf:$WhatIf
    }
}

function inner
{
    [cmdletbinding(supportsshouldprocess=$true)]
    param($s)

    process
    {   
        $pscmdlet.shouldprocess("inner $s", "ShouldProcess") | out-null
        "" | out-file "inner $s"
    }
}

"`n** NORMAL **"
outer normal
"`n** VERBOSE **"
outer verbose -Verbose
"`n** WHATIF **"
outer whatif -WhatIf

Saída:

** NORMAL **
VERBOSE: Performing operation "ShouldProcess" on Target "inner VerbosePassthru".
What if: Performing operation "ShouldProcess" on Target "inner WhatifPassthru".
What if: Performing operation "Output to File" on Target "inner WhatifPassthru".

** VERBOSE **
VERBOSE: Performing operation "ShouldProcess" on Target "outer verbose".
VERBOSE: Performing operation "ShouldProcess" on Target "inner VerbosePassthru".
What if: Performing operation "ShouldProcess" on Target "inner WhatifPassthru".
What if: Performing operation "Output to File" on Target "inner WhatifPassthru".

** WHATIF **
What if: Performing operation "ShouldProcess" on Target "outer whatif".
What if: Performing operation "Output to File" on Target "outer whatif".
What if: Performing operation "ShouldProcess" on Target "inner ImplicitPassthru".
What if: Performing operation "Output to File" on Target "inner ImplicitPassthru".
What if: Performing operation "ShouldProcess" on Target "inner VerbosePassthru".
What if: Performing operation "Output to File" on Target "inner VerbosePassthru".
What if: Performing operation "ShouldProcess" on Target "inner WhatifPassthru".
What if: Performing operation "Output to File" on Target "inner WhatifPassthru".

Aos meus olhos há várias curiosidades aqui:

  • Especificando -WhatIf:$foo vai sempre ligue $WhatIf no receptor (e sua callees), não importa o que $foo é.
  • Quando você não especificar -WhatIf "real" (sem restrição-lo a uma variável já existente), que se propaga para callees implicitamente.Não há necessidade de passagem ou splatting.
  • Ao contrário -WhatIf, explícito e Detalhado não em cascata para callees implicitamente.
  • Quando você tenta manualmente intermédio -Verbose:$foo, você vê o comportamento é semelhante para -WhatIf:$foo.Mas ele só afeta scripts de teste manualmente $psCmdlet.ShouldProcess() -- construído em cmdlets não são afetadas.

N. B.:Confirme se comporta de forma idêntica para o parâmetro WhatIf.Eu omiti-lo por questões de brevidade.

Pesquisando na web e Conectar-se, eu vejo quase nenhuma discussão mais aprofundada dos ShouldProcess comportamento (pró ou contra), como diz respeito a funções avançadas.Coisa mais próxima é um post de James O'Neill que recomenda a passagem de uma instância única de us $psCmdlet toda a pilha de chamadas.No entanto, ele o faz para solucionar um modo totalmente diferente de problema (evitando vários Confirmar prompts).Entretanto, quando você ficar com o $psCmdlet fornecido para cada função, eu vejo nenhum docs sobre o que esperar...muito menos padrões de design, melhores práticas, etc...

Foi útil?

Solução

Você realmente não pode se referir a $WhatIf $Detalhado, já que estes são sintetizadas para você i.e.estas variáveis não existem na sua função.Se o usuário especifica-las, em seguida, você pode obtê-los através de $PSBoundParameters mas se o usuário não especificar, então, obviamente, eles não serão neste hashtable.

Quando você passar um valor para um parâmetro PowerShell irá fazer o típico coerção processo para tentar converter o valor especificado para um bool.Desde $whatif não está definido este evals para $null, o que resulta no valor de opção a ser definido como $true.Este é, presumivelmente, porque ele vê o parâmetro é especificado explicitamente efetivamente sem valor, que é o equivalente a apenas especificando -Whatif com nenhum valor.Você pode ver isso quando você rastrear a ligação de parâmetro:

function Foo
{
    [CmdletBinding(SupportsShouldProcess=1)]
    param()

    Process
    {
        $PSBoundParameters
    }
}

Trace-Command -name ParameterBinding -expr {Foo -whatif:$xyzzy} -PSHost
DEBUG: BIND NAMED cmd line args [Foo]
DEBUG:   BIND arg [] to parameter [WhatIf]
DEBUG:     COERCE arg to [System.Management.Automation.SwitchParameter]
DEBUG:       Arg is null or not present, type is SWITCHPARAMTER, value is true.
DEBUG:         BIND arg [True] to param [WhatIf] SUCCESSFUL
DEBUG: BIND POSITIONAL cmd line args [Foo]
DEBUG: MANDATORY PARAMETER CHECK on cmdlet [Foo]
DEBUG: CALLING BeginProcessing
DEBUG: CALLING EndProcessing

O $WhatIfPreference e $VerbosePreference fica definido correctamente no exterior, com base em se exterior foi chamado com a opção-verbose ou -whatif.Eu posso ver que os valores de propagar para o interior bem.Parece que lá é um PowerShell bug com $pscmdlet.ShouldProcess.Ele não parece estar honrando o valor de r $VerbosePreference neste caso.Você pode tentar passar através Detalhado para o interior como para:

inner VerbosePassthru -Verbose:($VerbosePreference -eq 'Continue')

Outra opção é usar Get-Variável de Escopo-a assim:

function Outer
{
    [CmdletBinding(SupportsShouldProcess=1)]
    param()

    Process
    {
        $pscmdlet.ShouldProcess("Outer process", '') > $null
        inner
        #inner -Verbose:($VerbosePreference -eq 'Continue')
    }
}

function Inner
{
    [CmdletBinding(SupportsShouldProcess=1)]
    param()

    Process
    {
        $pscmdlet = (Get-Variable -Scope 1 -Name PSCmdlet).Value
        $pscmdlet.ShouldProcess("Inner process", '') > $null
        "Inner $VerbosePreference"
    }
}

Outer -Verbose

Eu não tenho certeza se eu gosto deste porque isso significa que você sabe o exterior é de 1 nível acima do interior.Você pode "andar" âmbito de aplicação da pilha olhando para o próximo PSCmdlet variável para a pilha.Isso efetivamente se livrar de ter que passar em PSCmdlet (que é bruto), mas ainda é um hack.Você deve considerar a apresentação de um bug no MS Conectar a esse respeito.

Outras dicas

Eu estava procurando escrever exatamente a mesma pergunta e estou escrevendo isso quase 7 anos depois. Estou surpreso que a equipe PowerShell da Microsoft ainda não tenha consertado isso. Eu reproduzi o problema com o PowerShell versão 6 Preview (versão mais recente).

Eu criei uma solução alternativa simples, isto é, dentro do Inner função, criamos e executamos um scriptblock, configurando o -Verbose bandeira verificando $VerbosePreference que é definido corretamente como Continue, mesmo que não seja respeitado por ShouldProcess:


Function Outer {
    [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")]
    param([string]$Name)

    Process {
        Write-Host "Outer called";
        Inner $Name
    }
}

Function Inner {
    [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")]
    param([string]$Name)

    Process {
        if (-not ($PSBoundParameters.ContainsKey('Verbose'))) {
            $PSBoundParameters.Add('Verbose', [bool]$VerbosePreference -eq 'Continue');
        }

        & {
            [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")]

            param([string]$Name)

            if ($PSCmdlet.ShouldProcess($Name, "Inner")) {
                Write-Host "Inner called";
            }
        } @PSBoundParameters;
    }
}

Export-ModuleMember *
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top