Script PowerShell: modo per implementare ShouldProcess consigliato quando chiamate di funzione sono annidati?
-
21-09-2019 - |
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 ...
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 *