2010-02-17 23 views
12

thử nghiệm kịch bản:Powershell scripting: cách được đề xuất để triển khai ShouldProcess khi các cuộc gọi hàm được lồng nhau?

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

Để mắt của tôi có một số oddities đây:

  • Xác định -WhatIf: $ foo sẽ luôn bật $ WhatIF trong callee (và callees của nó), không có vấn đề gì $ foo.
  • Khi bạn chỉ định -WhatIf "cho thực" (mà không ràng buộc nó vào một biến hiện tại), nó sẽ lan truyền đến callees ngầm. Không cần passthru hoặc splatting.
  • Không giống như -WhatIf, rõ ràng -Xác lý không xếp tầng để callees ngầm.
  • Khi bạn cố gắng theo cách thủ công passthru -Xoá: $ foo, bạn thấy hành vi tương tự như -WhatIf: $ foo. Nhưng nó chỉ ảnh hưởng đến các kịch bản thủ công kiểm tra $ psCmdlet.ShouldProcess() - được xây dựng trong các lệnh ghép ngắn không bị ảnh hưởng.

N.B.: Xác nhận hành vi giống với WhatIf. Tôi bỏ qua nó một cách ngắn gọn.

Tìm kiếm trên web và Kết nối, tôi thấy hầu như không có bất kỳ cuộc thảo luận sâu về hành vi ShouldProcess (pro hoặc con) liên quan đến các chức năng nâng cao. Điều gần nhất là a post from James O'Neill đề xuất chuyển một phiên bản $ psCmdlet duy nhất trong suốt ngăn xếp cuộc gọi. Tuy nhiên, ông làm như vậy để giải quyết một vấn đề hoàn toàn khác (tránh nhiều lời nhắc -Xác nhận). Trong khi đó, khi bạn gắn bó với tiêu chuẩn $ psCmdlet được cung cấp cho mỗi chức năng, tôi thấy không có tài liệu về những gì mong đợi ... nhiều mẫu thiết kế ít hơn, thực hành tốt nhất, v.v ...

Trả lời

11

Bạn thực sự không thể tham khảo $ WhatIf hoặc $ Verbose vì chúng được tổng hợp cho bạn, tức là các biến này không tồn tại trong hàm của bạn. Nếu người dùng chỉ định chúng thì bạn có thể nhận được chúng thông qua $ PSBoundParameters nhưng nếu người dùng không chỉ định thì rõ ràng là họ sẽ không có trong hashtable này.

Khi bạn chuyển giá trị cho một công tắc, PowerShell sẽ thực hiện quy trình ép buộc điển hình để cố gắng chuyển đổi giá trị được chỉ định thành một bool. Vì $ whatif không được định nghĩa, nó dẫn đến $ null, kết quả là giá trị chuyển đổi được đặt thành $ true. Điều này có lẽ bởi vì nó thấy chuyển đổi được xác định rõ ràng với hiệu quả không có giá trị đó là tương đương với chỉ chỉ định -Whatif không có giá trị. Bạn có thể thấy điều này khi bạn theo dõi các thông số ràng buộc:

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 

Các $ WhatIfPreference và $ VerbosePreference được thiết lập một cách thích hợp trong ngoài dựa trên việc bên ngoài được gọi với -verbose hoặc -whatif. Tôi có thể thấy rằng những giá trị đó truyền đến bên trong tốt. Dường như có lỗi PowerShell với $ pscmdlet.ShouldProcess. Nó dường như không tôn trọng giá trị của $ VerbosePreference trong trường hợp này. Bạn có thể thử qua -verbose để bên trong như vậy:

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

Một lựa chọn khác là sử dụng -Scope Get-Variable như vậy:

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 

Tôi không chắc chắn Tôi thích điều này vì nó ngụ ý mà bạn biết bên ngoài là 1 cấp độ bên trên.Bạn có thể "đi bộ" phạm vi ngăn xếp tìm kiếm biến PSCmdlet tiếp theo lên ngăn xếp. Điều này có hiệu quả được thoát khỏi phải vượt qua trong PSCmdlet (đó là tổng) nhưng nó vẫn là một hack. Bạn nên xem xét nộp một lỗi trên MS Connect về điều này.

+0

Bug nộp: https://connect.microsoft.com/PowerShell/feedback/details/535559/shouldprocess-cmdlets-the-verbose-preference-is-not-evaluated-in-callees Buộc null - > True cũng là một lỗi IMO, vì nó không xảy ra với các thông số chuyển đổi bình thường. Đã nộp riêng biệt @ https://connect.microsoft.com/PowerShell/feedback/details/535557/shouldprocess-synthetic-parameters-should-not-coerce-null-true –

+0

Đã bỏ phiếu. Cảm ơn bạn đã nộp đơn. –

+0

Các bạn làm việc tốt. – JasonMArcher

0

Tôi đang tìm cách viết chính xác cùng một câu hỏi và tôi viết bài này gần 7 năm sau đó. Tôi rất ngạc nhiên khi nhóm PowerShell của Microsoft chưa sửa lỗi này. Tôi đã sao chép vấn đề với PowerShell Version 6 Preview (phiên bản mới nhất).

tôi đã đưa ra một cách giải quyết đơn giản, đó là, bên trong Inner chức năng, chúng tôi tạo ra và chạy một scriptblock, thiết lập -Verbose cờ bằng cách kiểm tra $VerbosePreference mà được thiết lập một cách chính xác để Continue, mặc dù nó không được tôn trọng bởi 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

Điều đó có thể hoạt động. Tuy nhiên, mã soạn sẵn yêu cầu (trong mọi hàm!) Để đạt được hiệu ứng tinh tế này khá nặng. Bạn đang cố gắng đạt được một hành vi khác, nhưng không phải tất cả mã PowerShell đều sẽ tuân theo nó. Điều này có nghĩa rằng bạn đang thêm vào sự nhầm lẫn khi điều này làm việc khác nhau một cách tinh tế trong codebase của bạn. –

Các vấn đề liên quan