2014-12-02 71 views
1

我正在調查使用Runspaces進行並行運行。目前,我試圖從我可能運行的任何腳本中取回消息,以幫助我診斷問題,使用它們來源的相同流類型:詳細消息流中的詳細消息等等。到目前爲止,我可以得到Warning和詳細消息,但每當我嘗試使用寫入錯誤(甚至只是訪問ErrorRecord對象屬性),控制檯都會鎖定。爲什麼我無法從PowerShell.Streams.Error.add_DataAdded寫入錯誤?

這裏有一個示例來展示我所看到的內容。如果您取消註釋在$ps1.Streams.Error.add_DataAdded({})腳本塊它在這一點上鎖定了評論中的任意一個

VERBOSE: This is a verbose message. 
WARNING: This is a warning message. 
System.Management.Automation.ErrorRecord 
This is an error message. 

:目前,腳本生成四條消息。任何人都可以解釋爲什麼和/或給我一個解決方法/修復?

我這樣的事情後,我:

VERBOSE: This is a verbose message. 
WARNING: This is a warning message. 
C:\Work\me\PowerShell\Test-ReadPSDataStreams.ps1 : This is an error message. 
At line:1 char:1 
+ .\Test-ReadPSDataStreams.ps1 
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    + CategoryInfo   : NotSpecified: (:) [Write-Error], WriteErrorException 
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Test-ReadPSDataStreams.ps1 

這是腳本:

$VerbosePreference = 'Continue' 

$is1 = [InitialSessionState]::CreateDefault() 

$rs1 = [RunspaceFactory]::CreateRunspace($is1) 
$rs1.ApartmentState = 'STA' 
$rs1.ThreadOptions = 'ReuseThread' 
$rs1.Open() 

$ps1 = [PowerShell]::Create() 
$ps1.Runspace = $rs1 
$ps1.Streams.Verbose.add_DataAdded({ 
    Param (
     [Object]$sender, 
     [System.Management.Automation.DataAddedEventArgs]$e 
    ) 

    foreach ($item in $sender.ReadAll()) { 
     Write-Verbose $item.Message 
    } 
}) 

$ps1.Streams.Warning.add_DataAdded({ 
    Param (
     [Object]$sender, 
     [System.Management.Automation.DataAddedEventArgs]$e 
    ) 

    foreach ($item in $sender.ReadAll()) { 
     Write-Warning $item.Message 
    } 
}) 

$ps1.Streams.Error.add_DataAdded({ 
    Param (
     [Object]$sender, 
     [System.Management.Automation.DataAddedEventArgs]$e 
    ) 

    foreach ($item in $sender.ReadAll()) { 
     Write-Host $item.GetType() 
     Write-Host $item.ToString() 
     #Write-Host $item.ErrorDetails.Message 
     #Write-Error 'test' 
     #Write-Error $item.ToString() 
    } 
}) 

[void]$ps1.AddScript({ $VerbosePreference = 'Continue' }) 
[void]$ps1.AddScript({ Write-Verbose 'This is a verbose message.' }) 
[void]$ps1.AddScript({ Write-Warning 'This is a warning message.' }) 
[void]$ps1.AddScript({ Write-Error 'This is an error message.' }) 
$ps1.Invoke() 

如果有這樣做的另一種方式,我接受它!

+0

真的不明白你在做什麼,或者這個就足夠了,但是'寫主機$ item.Exception.Message'內您的問題塊似乎工作 – arco444 2014-12-02 16:37:53

+0

我這樣做,因爲否則這些消息沒有提交給開發者(即我)來幫助我調試。你所提供的工作,但我試圖讓它進入錯誤流,例如使用寫入錯誤。我會在問題中澄清這一點。 – user2871239 2014-12-02 16:57:29

回答

1

看看這是什麼,這可能有助於:

看看這有助於: http://mjolinor.wordpress.com/2014/06/03/invoke-scritptasync-v2/

如果我正確讀取的問題,它的設計正是您所描述的情況 - 測試腳本中運行空間環境並且能夠在執行期間將診斷信息寫入各種輸出流,然後在執行完成並且處理運行空間之後檢索該信息。

+0

這不是我正在尋找的。我已經從原始代碼中剔除了這些,以避免模糊問題。我之前沒有回覆過,因爲我仍在研究代碼對我工作的影響。不過,我已經投票贊成,因爲它幫助了我。 – user2871239 2014-12-10 16:12:23

+0

你需要什麼? – mjolinor 2014-12-10 16:14:02

+0

我正在用WPF做UI事情,並使用Runspace來運行後臺線程以保持UI的響應。我遇到了問題,我試圖將診斷信息回覆到主PowerShell會話,以便我可以看到發生了什麼問題。 – user2871239 2014-12-10 16:26:02

2

前言

我在我的平板電腦寫這個,所以請原諒我,如果格式不出來的權利。需要特別注意的是:我的平板電腦鍵盤沒有PowerShell用於轉義字符的反碼字符;因此,我在示例中將換行符寫爲「\ n」。

我的解決方案

我用你的技術發揮各地,這是我觀察到:

(1)工作錯誤流不打烊各地,通過其$錯誤變量訪問後臺主機的錯誤代替。爲此,請使用同步哈希表作爲SessionStateProxy來提供對後臺主機$ Error的訪問;看下面的例子。

→ $PRXY = [HashTable]::Synchronised(@{}) 

$rs1 = [RunspaceFactory]::CreateRunspace() 
→ $rs1.SessionStateProxy.SetVariable("PRXY",$PRXY) 
$rs1.Open() 

$ps1 = [PowerShell]::Create().AddScript({ 
→ $PRXY.Error = $Error 
    Write-Error "This is just a test from ps1" 
}) 
$ps1.Runspace = $rs1 

有關如何使用多線程同步的哈希表一篇好文章可以在這裏找到: http://learn-powershell.net/2013/04/19/sharing-variables-and-live-objects-between-powershell-runspaces/

其他事項

(2)使用寫入錯誤發送後臺運行空間的錯誤交互式控制檯將使用Write-Error cmdlet的InvocationInfo覆蓋錯誤的InvocationInfo。因此,我將錯誤對象直接發送到控制檯輸出。

(3)使用事件處理程序給了我一些問題。作爲解決方案,我使用了Register-ObjectEvent,並在while循環中使用事件輪詢來捕獲來自後臺運行空間的消息。 (4)使用stream。[type] .ReadAll()with:warning,verbose和debug會導致主機以與嘗試從錯誤流中讀取相同的方式掛起。爲了解決這個問題,我將流內容發送到管道ForEach循環,然後調用流Clear()方法。

完整示例

使用兩個運行空間來演示該概念。我想再次提醒你,這篇文章是用平板電腦寫的,所以不要期望下面的例子不需要先調試就可以運行。當我回到帶有ISE的真實計算機時,我將編輯下面的腳本來修復任何語法錯誤。

# Turn on verbose and debug messages 
$VerbosePreference = "Continue" 
$DebugPreference = "Continue" 

# Create background runspaces 
$PRXY = [HashTable]::Synchronised(@{}) 

$rs = @() 
For ($i = 0; $i -le 1; $i++) 
{ 
    $rs += [RunspaceFactory]::CreateRunspace() 
    $rs[$i].SessionStateProxy.SetVariable("PRXY", $PRXY) 
    $rs[$i].Open() 
} 

$sb1 = { 
    $PRXY.PS1.Error = $Error 
    $VerbosePreference = "Continue" 
    $DebugPreference = "Continue" 
    For ($i = 0; $i -lt 5; $i++) 
    { 
     $msg = "Test [$i]" 
     Write-Error $msg 
     Write-Warning $msg 
     Write-Verbose $msg 
     Write-Debug $msg 
     Start-Sleep -Milliseconds 750 
    } 
} 
$sb2 = { 
    $PRXY.PS2.Error = $Error 
    $VerbosePreference = "Continue" 
    $DebugPreference = "Continue" 
    For ($i = 0; $i -lt 5; $i++) 
    { 
     $msg = "Test [$i]" 
     Write-Error $msg 
     Write-Warning $msg 
     Write-Verbose $msg 
     Write-Debug $msg 
     Start-Sleep -Milliseconds 500 
    } 
} 
$PRXY.PS1 = @{} 
$ps1 = [PowerShell]::Create().AddScript($sb1) 
$ps1.Runspace = $rs[0] 

$PRXY.PS2 = @{} 
$ps2 = [PowerShell]::Create().AddScript($sb2) 
$ps2.Runspace = $rs[1] 

# Map event SourceIdentifiers to the runspace that produces the event 
$EventRegister = @{} 
$EventRegister.Error = @{} 
$EventRegister.Warning = @{} 
$EventRegister.Verbose = @{} 
$EvevtRegister.Debug = @{} 
$Registered = @() 

# Register PS1 -------------------- 
Register-ObjectEvent -InputObject $ps1.streams.error -EventName DataAdded 
ForEach ($id in (@(Get-EventSubscriber)).SourceIdentifier) 
{ 
    If ($id -notin $Registered) 
    { 
     $EventRegister.Error.Add($id, $ps1) 
     $Registered += $id 
    } 
} 
Register-ObjectEvent -InputObject $ps1.streams.warning -EventName DataAdded 
ForEach ($id in (@(Get-EventSubscriber)).SourceIdentifier) 
{ 
    If ($id -notin $Registered) 
    { 
     $EventRegister.Warning.Add($id, $ps1) 
     $Registered += $id 
    } 
} 
Register-ObjectEvent -InputObject $ps1.streams.verbose -EventName DataAdded 
ForEach ($id in (@(Get-EventSubscriber)).SourceIdentifier) 
{ 
    If ($id -notin $Registered) 
    { 
     $EventRegister.Verbose.Add($id, $ps1) 
     $Registered += $id 
    } 
} 
Register-ObjectEvent -InputObject $ps1.streams.debug -EventName DataAdded 
ForEach ($id in (@(Get-EventSubscriber)).SourceIdentifier) 
{ 
    If ($id -notin $Registered) 
    { 
     $EventRegister.Debug.Add($id, $ps1) 
     $Registered += $id 
    } 
} 

# Register PS2 ----------------------- 
Register-ObjectEvent -InputObject $ps2.streams.error -EventName DataAdded 
ForEach ($id in (@(Get-EventSubscriber)).SourceIdentifier) 
{ 
    If ($id -notin $Registered) 
    { 
     $EventRegister.Error.Add($id, $ps2) 
     $Registered += $id 
    } 
} 
Register-ObjectEvent -InputObject $ps2.streams.warning -EventName DataAdded 
ForEach ($id in (@(Get-EventSubscriber)).SourceIdentifier) 
{ 
    If ($id -notin $Registered) 
    { 
     $EventRegister.Warning.Add($id, $ps2) 
     $Registered += $id 
    } 
} 
Register-ObjectEvent -InputObject $ps2.streams.verbose -EventName DataAdded 
ForEach ($id in (@(Get-EventSubscriber)).SourceIdentifier) 
{ 
    If ($id -notin $Registered) 
    { 
     $EventRegister.Verbose.Add($id, $ps2) 
     $Registered += $id 
    } 
} 
Register-ObjectEvent -InputObject $ps2.streams.debug -EventName DataAdded 
ForEach ($id in (@(Get-EventSubscriber)).SourceIdentifier) 
{ 
    If ($id -notin $Registered) 
    { 
     $EventRegister.Debug.Add($id, $ps2) 
     $Registered += $id 
    } 
} 

$hndl_ps1 = $ps1.BeginInvoke() 
$hndl_ps2 = $ps2.BeginInvoke() 

While (!(hndl_ps1.IsCompleted) -or 
     !(hndl_ps2.IsCompleted)) 
{ 
    $Event = Wait-Event 
    If ($EventRegister.Error.ContainsKey($Event.SourceIdentifier)) 
    { 
     $psid = $EventRegister.Error[$Event.SourceIdentifier].InstanceId 
     $stamp = "$psid::$($Event.TimeGenerated)" 
     Write-Error $stamp 
     If ($psid -eq $ps1.InstanceId) 
     { 
      $PRXY.PS1.Error 
      $PRXY.PS1.Error.Clear() 
     } 
     If ($psid -eq $ps2.InstanceId) 
     { 
      $PRXY.PS2.Error 
      $PRXY.PS2.Error.Clear() 
     } 
     Remove-Event -EventIdentifier $Event.EventIdentifier 
     Continue 
    } 
    If ($EventRegister.Warning.ContainsKey($Event.SourceIdentifier)) 
    { 
     $stamp = "$($EventRegister.Warning[$Event.SourceIdentifier].InstanceId::" 
     $stamp += "$($Event.TimeGenerated)" 
     $EventRegister.Warning[$Event.SourceIdentifier].streams.warning | 
      ForEach {Write-Warning "{0}\n{1}\n\n" -f $stamp, $_} 
     $EventRegister.Warning[$Event.SourceIdentifier].streams.warning.Clear() 
     Remove-Event -EventIdentifier $Event.EventIdentifier 
     Continue 
    } 
    If ($EventRegister.Verbose.ContainsKey($Event.SourceIdentifier)) 
    { 
     $stamp = "$($EventRegister.Verbose[$Event.SourceIdentifier].InstanceId)::" 
     $stamp += "$($Event.TimeGenerated)" 
     $EventRegister.Verbose[$Event.SourceIdentifier].streams.verbose | 
      ForEach {Write-Verbose "{0}\n{1}\n\n" -f $stamp, $_} 
     $EventRegister.Verbose[$Event.SourceIdentifier].streams.verbose.Clear() 
     Remove-Event -EventIdentifier $Event.EventIdentifier 
     Continue 
    } 
    If ($EventRegister.Debug.ContainsKey($Event.SourceIdentifier)) 
    { 
     $stamp = "$($EventRegister.Debug[$Event.SourceIdentifier].InstanceId)::" 
     $stamp += "$($Event.TimeGenerated)" 
     $EventRegister.Debug[$Event.SourceIdentifier].streams.debug | 
      ForEach {Write-Debug "{0}\n{1}\n\n" -f $stamp, $_} 
     $EventRegister.Debug[$Event.SourceIdentifier].streams.debug.Clear() 
     Remove-Event -EventIdentifier $Event.EventIdentifier 
     Continue 
    } 
} 
$ps1.EndInvoke($hndl_ps1) 
$ps2.EndInvoke($hndl_ps2) 

# Optionally you can read the contents of all the streams after EndInvoke() 
# to see if any messages were missed. 
$ps1.streams.error 
$ps1.streams.warning.ReadAll() 
$ps1.streams.verbose.ReadAll() 
$ps1.streams.debug.ReadAll() 

$ps2.streams.error 
$ps2.streams.warning.ReadAll() 
$ps2.streams.verbose.ReadAll() 
$ps2.streams.debug.ReadAll() 

# Unregister subscribers if in the ISE 
Get-EventSubscriber | Unregister-Event -SourceIdentifier $_.SourceIdentifier 
相關問題