Script PowerShell: modo per implementare ShouldProcess consigliato quando chiamate di funzione sono annidati?

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

Domanda

script di test:

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

Output:

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

Al mio occhio ci sono diverse stranezze qui:

  • Specificando -WhatIf: $ pippo sarà sempre accendere $ WhatIf nel il chiamato (e le sue callees), non importa ciò che $ pippo è
  • .
  • Quando si specifica -WhatIf "per davvero" (senza limitare ad una variabile esistente), si propaga a callees implicitamente. Non c'è bisogno di passante o splatting.
  • A differenza -WhatIf, esplicito -Verbose non cascata callees implicitamente.
  • Quando si tenta di passante manualmente -Verbose: $ pippo, non vede comportamento è simile a -WhatIf: $ pippo. Ma riguarda solo gli script che mettono alla prova manualmente $ psCmdlet.ShouldProcess () -. Costruito nel cmdlet non sono interessati

NB :. Conferma comporta in modo identico a WhatIf. Ho omesso per brevità.

Vuoi prenotare online e Connect, vedo quasi nessuna discussione approfondita del comportamento ShouldProcess (pro o contro) connesse al funzioni avanzate. Cosa più vicina è un post da James O'Neill che raccomanda il passaggio di una singola istanza di $ PSCmdlet tutto lo stack di chiamate. Tuttavia, lo fa alla soluzione un problema completamente diverso (evitando molteplici richieste -confirm). Nel frattempo, quando si bastone con il $ PSCmdlet standard fornita ad ogni funzione, non vedo documentazione su cosa aspettarsi ... molto meno modelli di progettazione, le migliori pratiche, ecc ...

È stato utile?

Soluzione

Non si può davvero fare riferimento a $ WhatIf o $ verbose dal momento che questi sono sintetizzati per voi vale a dire queste variabili non esistono nella vostra funzione. Se l'utente li specifica allora si può ottenere a loro tramite $ PSBoundParameters ma se l'utente non ha specificato poi, ovviamente, non sarà in questa tabella hash.

Quando si passa un valore a uno switch PowerShell farà il processo tipico coercizione per tentare di convertire il valore specificato per un bool. Poiché $ WhatIf non è definita questa evals ad un $ nullo che determina il valore commutatore essendo impostato su $ true. Questo è presumibilmente perché sfrutta l'interruttore è specificato esplicitamente con efficacia alcun valore che è l'equivalente di una specifica -whatif senza valore. Si può vedere questo quando si traccia il parametro vincolante:

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

Il $ WhatIfPreference e $ VerbosePreference viene impostata in modo adeguato in esterno in base al fatto esterno è stato richiamato con verbose o -whatif. Posso vedere che quei valori si propagano per interiore più che bene. Sembrerebbe che ci sia un bug PowerShell con $ pscmdlet.ShouldProcess. Non sembra essere onorare il valore di $ VerbosePreference in questo caso. Si potrebbe provare a passare attraverso -Verbose per interno in questo modo:

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

Un'altra opzione è quella di utilizzare -Scope Get-Variabile in questo modo:

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

Non sono sicuro che mi piace questo perché implica che si sa esterno è di 1 livello superiore interno. Si potrebbe "camminare" lo stack portata alla ricerca della prossima variabili PSCmdlet lo stack. Questo in modo efficace si libera di dover passare in PSCmdlet (che è lordo) ma è ancora un hack. Si dovrebbe prendere in considerazione la presentazione di una bug su MS Connect su questo.

Altri suggerimenti

Stavo cercando di scrivere esattamente la stessa domanda, e sto scrivendo questo quasi 7 anni dopo. Mi sorprende che la squadra PowerShell di Microsoft non hanno ancora risolto questo. Ho riprodotto il problema con PowerShell versione 6 Preview (ultima versione).

Sono venuto su con una semplice soluzione, cioè, all'interno della funzione Inner, creiamo e gestiamo un scriptblock, impostando il flag -Verbose controllando $VerbosePreference che è impostato correttamente per Continue, anche se non è rispettato da 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 *
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top