2010-02-17 70 views
12

測試腳本:PowerShell腳本:當函數調用嵌套時推薦的方式來實現ShouldProcess?

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 

輸出:

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

爲了我的眼睛有下面介紹幾種奇特:

  • 指定-WhatIf:$ foo將總是接通$ WHATIF在被調用者(及其被調用者)中,不管$ foo是什麼。
  • 當您確實指定-WhatIf「for real」(不限制它到現有變量)時,它會隱式傳播給被調用者。無需中繼或潑濺。
  • 與-WhatIf不同,顯式-Verbose不隱式地級聯到被調用者。
  • 當您嘗試手動passthru -Verbose:$ foo時,您看到的行爲與-WhatIf:$ foo類似。但它隻影響手動測試$ psCmdlet.ShouldProcess() - 內建在cmdlet中的腳本不受影響。

N.B.:確認行爲與WhatIf相同。爲了簡潔起見,我省略了它。

在Web和Connect的搜索中,我幾乎沒有深入討論與高級函數有關的ShouldProcess行爲(pro或con)。最接近的是a post from James O'Neill,建議在整個調用堆棧中傳遞$ psCmdlet的單個實例。但是,他這樣做是爲了解決完全不同的問題(避免多個-Confirm提示)。同時,當您堅持提供給每個功能的標準$ psCmdlet時,我看不到有什麼期望的文檔...更少的設計模式,最佳實踐等等......

回答

11

您不能真正地引用$ WhatIf或$ Verbose,因爲它們是爲你合成的,即這些變量不存在於你的函數中。如果用戶指定了它們,那麼你可以通過$ PSBoundParameters獲得它們,但是如果用戶沒有指定那麼顯然它們不會在這個散列表中。

當您將值傳遞給開關時,PowerShell將執行典型的強制轉換過程以嘗試將指定值轉換爲布爾值。由於$ whatif沒有定義,所以這會轉化爲$ null,導致switch值被設置爲$ true。這大概是因爲它看到的切換是明確指定的有效沒有值,這是相當於只是指定沒有價值的Whatif。你可以看到這個當您跟蹤參數綁定:

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 

的$ WhatIfPreference和$ VerbosePreference被外基於外是否調用了-verbose或-whatif設置正確。我可以看到,這些值傳播到內部就好了。看起來有一個$ pscmdlet.ShouldProcess的PowerShell錯誤。在這種情況下,似乎沒有兌現$ VerbosePreference的價值。你可以嘗試通過-Verbose傳遞到內,像這樣:

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

另一種選擇是使用Get-可變-Scope像這樣:

​​

我不知道我喜歡這個,因爲它意味着你知道外層是內層的1層。你可以「走」範圍堆棧,尋找堆棧中的下一個PSCmdlet變量。這有效地擺脫了必須通過PSCmdlet(這是總的),但它仍然是一個黑客。你應該考慮在MS Connect上提交一個bug。

+0

的Bug提交:https://connect.microsoft.com/PowerShell/feedback/details/535559/shouldprocess-cmdlets-the-verbose-preference-is-not-evaluated-in-callees強制轉換爲空 - >真也是一個錯誤IMO,因爲它不會發生正常的開關參數。分別歸檔@ https://connect.microsoft.com/PowerShell/feedback/details/535557/shouldprocess-synthetic-parameters-should-not-coerce-null-true – 2010-02-23 08:17:20

+0

已投票。感謝您提交這些文件。 – 2010-02-24 18:34:52

+0

好工作的傢伙。 – JasonMArcher 2010-02-28 04:25:13

0

我正在尋找寫完全相同的問題,並且我在將近7年後寫了這篇文章。我很驚訝微軟的PowerShell團隊還沒有解決這個問題。我用PowerShell Version 6 Preview(最新版本)重現了這個問題。

我想出了一個簡單的解決方法,也就是Inner函數中,我們創建並運行scriptblock,通過檢查$VerbosePreference這是正確設置爲Continue,即使它不是由ShouldProcess尊重設置-Verbose標誌:


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

這可能有效。然而,要達到這種微妙效果所需的樣板代碼(在每個功能中)都是相當重要的。您試圖實現不同的行爲,但並非所有的PowerShell代碼都會遵守它。這意味着當你在代碼庫中工作的細微差別時,你會加入混淆。 – 2018-02-10 16:06:11