script Powershell: méthode recommandée pour mettre en œuvre ShouldProcess lorsque les appels de fonction sont imbriquées?

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

Question

script de 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

Sortie:

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

A mes yeux, il y a plusieurs bizarreries ici:

  • Spécification -WhatIf: $ foo sera toujours Activer WhatIf $ dans le callee (et ses fonctions appelées), peu importe ce que $ foo est
  • .
  • Lorsque vous spécifiez -WhatIf « pour de vrai » (sans contrainte à une variable existante), elle se propage à implicitement fonctions appelées. Pas besoin de passthru ou splatting.
  • Contrairement à -WhatIf, -Verbose explicite ne cascade pas implicitement fonctions appelées.
  • Lorsque vous essayez de PASSTHRU manuellement -Verbose: $ foo, vous ne voyez le comportement est similaire à -WhatIf: $ foo. Mais il ne touche que les scripts qui testent manuellement $ psCmdlet.ShouldProcess () -. Construite en cmdlets ne sont pas affectés

N.B. :. Confirmer le comportement identique à WhatIf. J'omis pour abréger.

Recherche sur le web et Connect, je ne vois guère de discussion approfondie du comportement ShouldProcess (pour ou contre) comme se rapporte à des fonctions avancées. Chose la plus proche est un poste de James O'Neill qui recommande le passage d'une seule instance de psCmdlet $ tout au long de la pile d'appels. Cependant, il le fait de contourner un problème tout à fait différent (éviter plusieurs invites -confirment). Pendant ce temps, quand vous vous en tenez à la psCmdlet standard $ fourni à chaque fonction, je ne vois pas de document sur ce qu'il faut attendre ... beaucoup moins de modèles de conception, les meilleures pratiques, etc ...

Était-ce utile?

La solution

Vous ne pouvez pas vraiment se référer WhatIf $ ou $ verbose puisque ceux-ci sont synthétisés pour vous dire ces variables n'existent pas dans votre fonction. Si l'utilisateur spécifie les alors vous pouvez obtenir à eux via $ PSBoundParameters mais si l'utilisateur n'a pas spécifié alors évidemment ils ne seront pas dans ce Hashtable.

Lorsque vous passez une valeur à un commutateur PowerShell fera le processus de contrainte typique pour tenter de convertir la valeur spécifiée à un bool. Depuis whatif $ est pas défini ce evals à une valeur nulle de $, ce qui se traduit par la valeur de commutation étant réglé sur $ true. Ceci est sans doute parce qu'il voit le commutateur est explicitement spécifié efficacement sans valeur qui est l'équivalent de seulement spécifiant -whatIf sans valeur. Vous pouvez voir quand on trace le paramètre de liaison:

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

Le WhatIfPreference $ et $ VerbosePreference obtient l'ensemble de manière appropriée en extérieur selon que externe a été appelé avec -verbose ou -whatif. Je peux voir que ces valeurs se propagent à très bien intérieure. Il semblerait qu'il y ait un bug avec PowerShell $ pscmdlet.ShouldProcess. Il ne semble pas honorer la valeur de VerbosePreference $ dans ce cas. Vous pouvez essayer de passer par -Verbose à intérieur comme ceci:

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

Une autre option consiste à utiliser Get-Variable -Scope comme ceci:

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

Je ne suis pas sûr que je l'aime parce que cela implique que vous savez externe est de 1 niveau au-dessus intérieur. Vous pouvez « marcher » la pile portée à la recherche de la prochaine variable PSCmdlet jusqu'à la pile. Cela se débarrasser efficacement de devoir passer PSCmdlet (ce qui est brut), mais il est encore un hack. Vous devriez envisager de déposer un bug sur MS Connect à ce sujet.

Autres conseils

Je cherchais à écrire exactement la même question, et je vous écris ce presque 7 ans plus tard. Je suis surpris que l'équipe PowerShell de Microsoft n'a pas encore résolu ce. J'ai reproduit la question avec PowerShell Version 6 Preview (dernière version).

Je suis venu avec une solution simple, qui est, à l'intérieur de la fonction Inner, nous créons et exécutons un scriptblock, mettre le drapeau -Verbose en vérifiant $VerbosePreference qui est correctement réglé Continue, même si elle est pas respectée par 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 *
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top