2012-01-05 22 views
10

Trong PowerShell v2, dòng sau:Chọn đối tượng dừng đường ống trong PowerShell v3 như thế nào?

1..3| foreach { Write-Host "Value : $_"; $_ }| select -First 1 

sẽ hiển thị:

Value : 1 
1 
Value : 2 
Value : 3 

Vì tất cả các yếu tố này được đẩy xuống các đường ống dẫn. Tuy nhiên, trong v3 dòng trên chỉ hiển thị:

Value : 1 
1 

Các đường ống được dừng lại trước 2 và 3 được gửi đến Foreach-Object (Lưu ý: -Wait switch cho Select-Object cho phép tất cả các yếu tố để đạt được khối foreach).

Làm cách nào để Select-Object dừng kênh và bây giờ tôi có thể dừng đường ống từ foreach hoặc từ chức năng của riêng tôi không?

Chỉnh sửa: Tôi biết tôi có thể quấn đường ống trong vòng lặp ... trong khi và tiếp tục thoát ra khỏi đường ống. Tôi cũng đã phát hiện ra rằng trong v3 tôi có thể làm một cái gì đó như thế này (nó không hoạt động trong v2):

function Start-Enumerate ($array) { 
    do{ $array } while($false) 
} 

Start-Enumerate (1..3)| foreach {if($_ -ge 2){break};$_}; 'V2 Will Not Get Here' 

Nhưng Select-Object không yêu cầu một trong những kỹ thuật này vì vậy tôi đã hy vọng rằng có một cách để dừng đường ống từ một điểm duy nhất trong đường ống.

+1

Vì vậy, bạn đang tìm kiếm StopUpstreamCommandsException, nhưng bạn có thể không sử dụng nó vì nó là nội bộ. Đây là gợi ý kết nối MS cho nó: https://connect.microsoft.com/PowerShell/feedback/details/768650/enable-users-to-stop-pipeline-making-stopupstreamcommandsexception-public –

+1

Cảm ơn, @LarsTruijens đã chỉ cho tôi cho điều đó; Tôi đã bình chọn nó. – Rynant

Trả lời

3

Kiểm tra bài này về cách bạn có thể hủy một đường ống dẫn:
http://powershell.com/cs/blogs/tobias/archive/2010/01/01/cancelling-a-pipeline.aspx

Trong PowerShell 3.0 đó là một sự cải tiến động cơ. Từ các mẫu thư mục CTP1 ('\ Engines Demo \ misc \ ConnectBugFixes.ps1'):

# Connect Bug 332685 
# Select-Object optimization 
# Submitted by Shay Levi 
# Connect Suggestion 286219 
# PSV2: Lazy pipeline - ability for cmdlets to say "NO MORE" 
# Submitted by Karl Prosser 

# Stop the pipeline once the objects have been selected 
# Useful for commands that return a lot of objects, like dealing with the event log 

# In PS 2.0, this took a long time even though we only wanted the first 10 events 
Start-Process powershell.exe -Args '-Version 2 -NoExit -Command Get-WinEvent | Select-Object -First 10' 

# In PS 3.0, the pipeline stops after retrieving the first 10 objects 
Get-WinEvent | Select-Object -First 10 
+0

Vâng, tôi đã nhìn thấy cả hai. Tôi đã cập nhật câu hỏi của mình. – Rynant

+0

Theo như tôi có thể nói nó ném một ngoại lệ StopUpstreamCommandsException, mà là rất giống với những gì Tobias đang làm trong bài tôi đã đề cập. –

+1

Nhưng không giống như PipelineStoppedException, 'Select-Object' không ngăn các lệnh downstream hoàn thành. Tôi muốn có thể ngăn chặn các đường ống mà không yêu cầu người dùng biết rằng họ phải bọc đường ống trong một thời gian hoặc cố gắng nắm bắt, nhưng tôi cho rằng không thể sử dụng StopUpstreamCommandsException vì nó là một kiểu riêng tư. – Rynant

1

Tôi biết rằng ném một PipelineStoppedException dừng các đường ống dẫn. Ví dụ sau đây sẽ mô phỏng những gì bạn nhìn thấy với Select -first 1 trong v3.0, trong v2.0:

filter Select-Improved($first) { 
    begin{ 
     $count = 0 
    } 
    process{ 
     $_ 
     $count++ 
     if($count -ge $first){throw (new-object System.Management.Automation.PipelineStoppedException)} 
    } 
} 

trap{continue} 
1..3| foreach { Write-Host "Value : $_"; $_ }| Select-Improved -first 1 
write-host "after" 
+0

Bạn có lỗi đánh máy: $ fist> $ trước. Và 'mới' nên là đối tượng mới. –

+0

@ShayLevy - Cảm ơn, sửa chữa :) – manojlds

+1

Vấn đề tôi có với việc ném một PipelineStoppedException là các lệnh tiếp tục xuống đường ống không kết thúc quá trình xử lý. Điều này hoạt động: '1..5 | chọn-đầu tiên 3 | measure', nhưng điều này không: '1..5 | Select-Improved -first 3 | đo lường ' – Rynant

3

Sau khi thử một số phương pháp, bao gồm ném StopUpstreamCommandsException, ActionPreferenceStopException, và PipelineClosedException, gọi $ PSCmdlet.ThrowTerminatingError và $ ExecutionContext. Host.Runspace.GetCurrentlyRunningPipeline(). Stopper.set_IsStopping ($ true) Cuối cùng tôi thấy rằng chỉ sử dụng đối tượng chọn là thứ duy nhất không hủy bỏ toàn bộ kịch bản (so với chỉ là đường dẫn). [Lưu ý rằng một số các mặt hàng nêu trên yêu cầu truy cập đến các thành viên tư nhân, mà tôi truy cập thông qua phản ánh.]

# This looks like it should put a zero in the pipeline but on PS 3.0 it doesn't 
function stop-pipeline { 
    $sp = {select-object -f 1}.GetSteppablePipeline($MyInvocation.CommandOrigin) 
    $sp.Begin($true) 
    $x = $sp.Process(0) # this call doesn't return 
    $sp.End() 
} 

Phương pháp mới sau dựa trên nhận xét của OP. Thật không may phương pháp này phức tạp hơn nhiều và sử dụng các thành viên riêng tư. Ngoài ra tôi không biết làm thế nào mạnh mẽ này - Tôi chỉ có ví dụ của OP để làm việc và dừng lại ở đó.Vì vậy, FWIW: Mã

# wh is alias for write-host 
# sel is alias for select-object 

# The following two use reflection to access private members: 
# invoke-method invokes private methods 
# select-properties is similar to select-object, but it gets private properties 

# Get the system.management.automation assembly 
$smaa=[appdomain]::currentdomain.getassemblies()| 
     ? location -like "*system.management.automation*" 

# Get the StopUpstreamCommandsException class 
$upcet=$smaa.gettypes()| ? name -like "*upstream*" 

filter x { 
    [CmdletBinding()] 
    param(
    [parameter(ValueFromPipeline=$true)] 
    [object] $inputObject 
) 
    process { 
    if ($inputObject -ge 5) { 
     # Create a StopUpstreamCommandsException 
     $upce = [activator]::CreateInstance($upcet,@($pscmdlet)) 

     $PipelineProcessor=$pscmdlet.CommandRuntime|select-properties PipelineProcessor 
     $commands = $PipelineProcessor|select-properties commands 
     $commandProcessor= $commands[0] 

     $null = $upce.RequestingCommandProcessor|select-properties * 

     $upce.RequestingCommandProcessor.commandinfo = 
      $commandProcessor|select-properties commandinfo 

     $upce.RequestingCommandProcessor.Commandruntime = 
      $commandProcessor|select-properties commandruntime 

     $null = $PipelineProcessor| 
      invoke-method recordfailure @($upce, $commandProcessor.command) 

     1..($commands.count-1) | % { 
     $commands[$_] | invoke-method DoComplete 
     } 

     wh throwing 
     throw $upce 
    } 
    wh "< $inputObject >" 

    $inputObject 
    } # end process 
    end { 
    wh in x end 
    } 
} # end filter x 

filter y { 
    [CmdletBinding()] 
    param(
    [parameter(ValueFromPipeline=$true)] 
    [object] $inputObject 
) 
    process { 
    $inputObject 
    } 
    end { 
    wh in y end 
    } 
} 

1..5| x | y | measure -Sum 

PowerShell để lấy giá trị PipelineProcessor qua phản ánh:

$t_cmdRun = $pscmdlet.CommandRuntime.gettype() 
# Get pipelineprocessor value ($pipor) 
$bindFlags = [Reflection.BindingFlags]"NonPublic,Instance" 
$piporProp = $t_cmdRun.getproperty("PipelineProcessor", $bindFlags) 
$pipor=$piporProp.GetValue($PSCmdlet.CommandRuntime,$null) 

đang Powershell để gọi phương pháp thông qua phản ánh:

$proc = (gps)[12] # semi-random process 
$methinfo = $proc.gettype().getmethod("GetComIUnknown", $bindFlags) 
# Return ComIUnknown as an IntPtr 
$comIUnknown = $methinfo.Invoke($proc, @($true)) 
+0

Cải thiện định dạng, vui lòng. –

+0

Điều này không làm những gì tôi đang tìm kiếm. '1..5 | chọn -Đầu tiên 3 | measure -Sum' trả về kết quả, nhưng '1..5 | % {if ($ _ -ge 4) {stop-pipeline}} | measure -Sum' thì không. Tôi muốn dừng các mục mới được gửi qua đường ống, nhưng cho phép đường dẫn xử lý xong. – Rynant

+1

Bạn có thể đưa mã vào cho các hàm 'invoke-method' và' select-properties' không? Mã được cung cấp sẽ không hoạt động nếu không có các chức năng đó. – Rynant

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