2010-02-17 10 views
12

Test:Skrypty typu powershell: zalecany sposób implementacji procedury ShouldProcess podczas zagnieżdżania wywołań funkcji? Skrypt

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 

wyjściowa:

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

Aby oka istnieje kilka osobliwości tutaj:

  • Określanie -whatIf: $ foo zawsze kolej na $ whatIf w callee (i jego callees), bez względu na to, co jest $ foo.
  • Po wybraniu opcji "Co prawda" na rzeczywistą "(bez ograniczania istniejącej zmiennej), propaguje do pośredników niejawnie. Nie ma potrzeby stosowania passthru lub splattingu.
  • W przeciwieństwie do -WhatIf, explicite -Verbose nie kaskadowo przechodzi do pośredników.
  • Przy próbie ręcznego podania hasła -Verbose: $ foo, widzisz zachowanie podobne do -WhatIf: $ foo. Ale wpływa tylko na skrypty, które testują ręcznie $ psCmdlet. Nie ma to wpływu na budowane w cmdletach polecenia `ShouldProcess().

N.B.: Potwierdzenie zachowuje się identycznie jak WhatIf. Pominąłem to dla zwięzłości.

Przeszukując internet i Connect, nie widzę prawie żadnej dogłębnej dyskusji na temat zachowania ShouldProcess (pro lub con) w odniesieniu do funkcji zaawansowanych. Najbliżej jest a post from James O'Neill, który zaleca przekazanie pojedynczej instancji $ psCmdlet przez cały stos wywołań. Robi to jednak, aby obejść zupełnie inny problem (unikając wielokrotnych monitów potwierdzających). Tymczasem, gdy pozostaniesz ze standardowym $ psCmdlet dostarczonym do każdej funkcji, nie widzę dokumentów na temat tego, czego się spodziewać ... znacznie mniej wzorów, najlepszych praktyk itp.

Odpowiedz

11

Nie można tak naprawdę odnosić się do $ WhatIf or $ Verbose, ponieważ są one syntetyzowane dla ciebie, tzn. Te zmienne nie istnieją w twojej funkcji. Jeśli użytkownik je określi, możesz uzyskać do nich dostęp za pomocą $ PSBoundParameters, ale jeśli użytkownik tego nie określił, to oczywiście nie będzie w tym hoście.

Po przekazaniu wartości do przełącznika PowerShell wykona typowy proces przymusu, aby spróbować przekonwertować podaną wartość na wartość bool. Ponieważ wartość $ whatif nie jest zdefiniowana, wartość ta wynosi zero, co powoduje, że wartość przełącznika jest ustawiona na $ true. Jest tak prawdopodobnie dlatego, że widzi wyraźnie, że przełącznik jest jawnie określony bez żadnej wartości, co jest równoważne z określeniem -Whatif bez wartości. Widać to, gdy śledzenia parametr Oprawa:

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 

dolarów WhatIfPreference i $ VerbosePreference zostanie odpowiednio ustawione w oparciu o zewnętrzną czy zewnętrzna została wywołana z -verbose lub -whatIf. Widzę, że te wartości rozprzestrzeniają się do wewnątrz w porządku. Wygląda na to, że istnieje błąd PowerShell z $ pscmdlet.ShouldProcess. Nie wydaje się, aby w tym przypadku honorować wartość $ VerbosePreference. Można spróbować przejazdem -verbose aby wewnętrzna tak:

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

Inną opcją jest użycie Get-Variable -Zakres tak:

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 

Nie jestem pewien, mi się podoba, ponieważ implikuje że wiesz, że zewnętrzny jest 1 poziom ponad wewnętrzną.Możesz "chodzić" po stosie teleskopów, szukając następnej zmiennej PSCmdlet na stosie. To skutecznie pozbywa się konieczności przekazywania w PSCmdlet (co jest brutto), ale wciąż jest hack. Powinieneś rozważyć zgłoszenie błędu w MS Connect w związku z tym.

+0

Bug złożony: https://connect.microsoft.com/PowerShell/feedback/details/535559/shouldprocess-cmdlets-the-verbose-preference-is-not-evaluated-in-callees Zmuszanie null - > True to również błąd IMO, ponieważ nie ma on normalnych parametrów przełącznika. Zapisano osobno @ https://connect.microsoft.com/PowerShell/feedback/details/535557/shouldprocess-synthetic-parameters-should-not-coerce-null-true –

+0

Zagłosowano. Dziękuję za zgłoszenie. –

+0

Dobra robota chłopaki. – JasonMArcher

0

Szukałem dokładnie tego samego pytania i piszę to prawie 7 lat później. Jestem zaskoczony, że zespół PowerShell Microsoftu jeszcze tego nie naprawił. Odtworzyłem ten problem za pomocą Podglądu PowerShell w wersji 6 (najnowsza wersja).

mam wymyślić proste obejście, czyli wewnątrz funkcji Inner, tworzymy i uruchomić scriptblock, ustawiając flagę -Verbose sprawdzając $VerbosePreference który jest prawidłowo ustawiony Continue, mimo że nie jest przestrzegana przez 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 * 
+0

To może zadziałać. Jednak wymagany kod (w każdej funkcji!), Aby osiągnąć ten subtelny efekt, jest dość ciężki. Próbujesz osiągnąć odmienne zachowanie, ale nie wszystkie kody PowerShell będą się do niego stosować. Oznacza to, że dodajesz zamieszanie, gdy działa ono nieco różnie w twojej bazie kodów. –

Powiązane problemy