Powershell-Scripting: empfohlene Weg Should zu implementieren, wenn Funktionsaufrufe geschachtelt sind?

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

Frage

Test-Skript:

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

Ausgabe:

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

In meinen Augen gibt es mehrere Merkwürdigkeiten hier:

  • Die Angabe -WhatIf:. $ Foo wird immer einschalten $ WhatIf in den Angerufenen (und seine Angerufenen), egal, was $ foo ist
  • Wenn Sie das tun angeben -WhatIf „für echte“ (ohne es zu einem vorhandenen Variablen Zwang) breitet es Aufgerufenen implizit. Keine Notwendigkeit für Durchgangs- oder splatting.
  • Im Gegensatz zu -WhatIf expliziter -Verbose nicht zu Aufgerufenen kaskadieren implizit.
  • Wenn Sie versuchen, manuell passthru -Verbose: $ foo, Sie sehen Verhalten -WhatIf ähnlich ist: $ foo. Aber es wirkt sich nur auf Skripte, die manuell test $ psCmdlet.ShouldProcess (.) - gebaut in Cmdlets sind nicht betroffen

N. B . Bestätigen Sie verhält sich zu WhatIf identisch. Ich weggelassen es der Kürze halber.

Suche im Web und Connect, sehe ich kaum eine eingehende Diskussion über Should Verhalten (pro oder contra) als bezieht sich auf erweiterte Funktionen. Nächste Sache ist ein Beitrag von James O'Neill dass empfiehlt während des Call-Stack eine einzelne Instanz von $ psCmdlet vorbei. Aber er hat so ein ganz anderes Problem (Vermeidung mehr -Confirm Aufforderungen) zu umgehen. Inzwischen, wenn Sie mit dem Standard $ psCmdlet jede Funktion zur Verfügung gestellt bleiben, sehe ich keine Dokumente auf, was zu erwarten ... viel weniger Design Patterns, Best Practices, etc ...

War es hilfreich?

Lösung

Sie können nicht wirklich beziehen sich auf $ WhatIf oder $ Verbose, da diese für Sie synthetisiert werden, das heißt diese Variablen nicht existieren in Ihrer Funktion. Wenn der Benutzer festlegt sie dann kann man sich über $ PSBoundParameters zu bekommen, aber wenn der Benutzer dann nicht angegeben hat offensichtlich werden sie nicht in dieser Hash-Tabelle sein.

Wenn Sie einen Wert an einen Switch Powershell passieren den typischen Zwang Prozess tun zu versuchen, auf den angegebenen Wert zu einem bool zu konvertieren. Da $ whatif ist das nicht evals zu einem $ null definiert, die Ergebnisse in dem Schaltwert auf $ true eingestellt wird. Dies ist vermutlich, weil es der Schalter sieht explizit mit effektiv kein Wert angegeben ist, das ist das Äquivalent von nur ohne Wert -WhatIf angeben. Sie können dies sehen, wenn Sie die Parameter Spuren Bindung:

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

Das $ WhatIfPreference und $ VerbosePreference wird entsprechend gesetzt in äußeren basierend darauf, ob Außen wurde mit -verbose oder -whatif genannt. Ich kann sehen, dass diese Werte zu inneren gerade fein ausbreiten. Es scheint, dass es ein Powershell-Fehler mit $ pscmdlet.ShouldProcess ist. Es scheint nicht, den Wert von $ VerbosePreference in diesem Fall zu ehren. Sie könnten versuchen, durch -Verbose zu inneren vorbei in etwa so:

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

Eine weitere Möglichkeit ist die Verwendung Get-Variable -Scope etwa so:

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

Ich bin mir nicht sicher, ob ich so, weil es bedeutet, dass Sie wissen, Außen 1 Ebene über Innen. Sie könnten „zu Fuß“ der Stapel Rahmen für die nächste PSCmdlet Variable auf den Stapel suchen. Dies wird effektiv los in PSCmdlet passieren zu müssen (was grob ist), aber es ist immer noch ein Hack. Sie sollten die Einreichung einen Fehler auf MS Connect darüber berücksichtigen.

Andere Tipps

Ich war auf der Suche genau die gleiche Frage zu schreiben, und ich schreibe diese fast 7 Jahre später. Ich bin überrascht, dass Microsofts Powershell-Team dies noch nicht festgelegt haben. Ich habe das Problem mit Powershell Version 6 Vorschau wiedergegeben (neueste Version).

Ich habe kommen mit einem einfachen Problem zu umgehen, die innerhalb der Inner Funktion ist, wir schaffen und ein scriptblock laufen, die -Verbose Flags von $VerbosePreference Überprüfung des korrekten Continue gesetzt ist, auch wenn es nicht von ShouldProcess respektiert wird:


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 *
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top