Powershell secuencias de comandos: forma de implementar ShouldProcess recomienda cuando se anidan las llamadas de función?

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

Pregunta

script de prueba:

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

Salida:

** 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".

Para mi ojo hay varias rarezas aquí:

  • Especificación -WhatIf: $ foo siempre activar $ WhatIf en el destinatario de la llamada (y sus receptores de llamadas), no importa lo que $ foo es
  • .
  • Cuando haces especificar -WhatIf "de verdad" (sin restringir a una variable existente), que se propaga a receptores de llamadas de forma implícita. No hay necesidad de tránsito o splatting.
  • A diferencia de -WhatIf, explícita -Verbose no en cascada a receptores de llamadas de forma implícita.
  • Cuando intenta PASSTHRU manualmente -Verbose: $ foo, usted ve el comportamiento es similar al -WhatIf: $ foo. Pero sólo afecta a las secuencias que ponen a prueba manualmente $ psCmdlet.ShouldProcess (-). Construidas en cmdlets no están afectados

N.B . Confirmar comporta idéntico al WhatIf. Omití que por razones de brevedad.

Búsqueda de la web y Connect, veo casi ninguna discusión en profundidad del comportamiento ShouldProcess (favor o en contra) que se refiere a las funciones avanzadas. Lo más parecido es un puesto de James O'Neill que recomienda pasar una instancia única de $ psCmdlet lo largo de la pila de llamadas. Sin embargo, lo hace para solucionar un problema totalmente diferente (evitando múltiples indicaciones -Confirm). Mientras tanto, cuando se cumple con el estándar de $ psCmdlet proporcionado a cada función, no veo documentos sobre qué esperar ... y mucho menos los patrones de diseño, mejores prácticas, etc ...

¿Fue útil?

Solución

No se puede referir a $ o $ WhatIf detallado ya que estos se sintetizan para usted es decir, estas variables no existen en su función. Si el usuario especifica que entonces se puede llegar a ellos a través de $ PSBoundParameters pero si el usuario no especificó entonces, evidentemente, no va a estar en esta tabla hash.

Cuando se pasa un valor a un interruptor PowerShell hará el proceso de coerción típica para intentar convertir el valor especificado a un bool. Desde $ whatif no se define esta evals a un $ nula que se traduce en el valor del interruptor se establece en $ true. Esto es presumiblemente debido a que ve el interruptor se especifica explícitamente con eficacia sin valor, que es el equivalente de solo especificando -WhatIf sin valor. Esto se puede ver cuando se traza la unión del 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

El WhatIfPreference $ y $ VerbosePreference consigue el sistema apropiadamente en exterior en función de si se llama externa con -verbose o whatif. Puedo ver que esos valores se propagan a bien sólo interior. Parecería que hay un error PowerShell con $ pscmdlet.ShouldProcess. No parece estar en honor al valor de $ VerbosePreference en este caso. Usted podría tratar de pasar por -Verbose al interior de esta manera:

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

Otra opción es utilizar -Ámbito Get-Variable de esta manera:

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

No estoy seguro de que me gusta esto porque implica que conoce externa es de 1 nivel por encima de interior. Se podía "caminar" la pila alcance en busca de la siguiente variable PSCmdlet la pila. Esto efectivamente se libra de tener que pasar en PSCmdlet (que es bruto) pero aún así es un truco. Usted debe considerar la presentación de un error en MS Conectar acerca de esto.

Otros consejos

Yo estaba buscando a escribir exactamente la misma pregunta, y yo estoy escribiendo esto casi 7 años después. Me sorprende que el equipo de PowerShell de Microsoft no han fijado esto todavía. He reproducido el problema con PowerShell versión 6 Vista Previa (última versión).

He llegado con una sencilla solución, es decir, dentro de la función Inner, a crear y ejecutar un scriptblock, el establecimiento del indicador -Verbose comprobando $VerbosePreference que está establecido correctamente en Continue, a pesar de que no es respetado 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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top