我正在使用 PowerShell SDK 執行腳本,它使用所有不同的流(資訊、警告、詳細資訊……)。我可以正確地捕獲它們的輸出,但不是按照它們生成的順序。例如,這是一個控制臺應用程式(C#、.NET 7,安裝了 NuGet 包Microsoft.PowerShell.SDK):
using System.Management.Automation.Runspaces;
var runSpace = RunspaceFactory.CreateRunspace(InitialSessionState.CreateDefault());
runSpace.Open();
var instance = System.Management.Automation.PowerShell.Create(runSpace);
instance.AddScript("""
$VerbosePreference = 'Continue'
Write-Verbose "Line 1"
Write-Output "Line 2"
Write-Verbose "Line 3"
Write-Information "Line 4"
Write-Information "Line 5"
Write-Verbose "Line 6"
"""
);
var output = instance.Invoke();
foreach (var o in output)
{
Console.WriteLine($"[N]: {o}");
}
foreach (var v in instance.Streams.Verbose)
{
Console.WriteLine($"[V]: {v}");
}
foreach (var i in instance.Streams.Information)
{
Console.WriteLine($"[I]: {i}");
}
如您所見,我在不同的流上回傳不同的結果。當然,當我像那樣輸出它們時,它們的順序不再正確:
[N]: Line 2
[V]: Line 1
[V]: Line 3
[V]: Line 6
[I]: Line 4
[I]: Line 5
我一直在查看 、 等提供的物件instance.Streams.Information-instance.Streams.Verbose但我找不到可以讓我對它們進行排序的屬性。有趣的是instance.Streams.Information有一個TimeGenerated,但它在所有其他流物件中都沒有!
所以我很困惑如何才能做到這一點,是否有可能根據它們的生成時間對它們進行排序?
uj5u.com熱心網友回復:
您需要預先訂閱每個流集合上的相關事件,而不是等到執行完成。
為此,請在每個相關屬性上為DataAdded事件注冊處理程式:instance.Streams.*
var runSpace = RunspaceFactory.CreateRunspace(InitialSessionState.CreateDefault());
runSpace.Open();
using (var instance = System.Management.Automation.PowerShell.Create(runSpace))
{
instance.AddScript("""
$VerbosePreference = 'Continue'
Write-Verbose "Line 1"
Write-Output "Line 2"
Write-Verbose "Line 3"
Write-Information "Line 4"
Write-Information "Line 5"
Write-Verbose "Line 6"
"""
);
# register event handler for the DataAdded event on each relevant stream collection
instance.Streams.Verbose.DataAdded = ConsumeStreamOutput;
instance.Streams.Information.DataAdded = ConsumeStreamOutput;
# now create a data collection for standard output
var outputCollection = new PSDataCollection<PSObject>();
# ... and register the event handler on that too
outputCollection.DataAdded = ConsumeStreamOutput;
# ... and now we're finally ready to invoke the script
instance.Invoke();
}
現在我們只需要定義和實作ConsumeStreamOutput處理事件的方法。它需要根據寫入的流期望不同型別的輸出。
static void ConsumeStreamOutput(object sender, DataAddedEventArgs evtArgs)
{
var data = ((System.Collections.IList)sender)[evtArgs.Index];
switch (data)
{
case VerboseRecord vr:
WriteLine($"Received Verbose stream data : '{vr}'");
break;
case InformationRecord ir:
WriteLine($"Received Information stream data : '{ir}'");
break;
default:
WriteLine($"Received standard output : '{data}'");
break;
}
}
上面應該給你這樣的輸出:
Received Verbose stream data : 'Line 1'
Received standard output : 'Line 2'
Received Verbose stream data : 'Line 3'
Received Information stream data : 'Line 4'
Received Information stream data : 'Line 5'
Received Verbose stream data : 'Line 6'
當然,如果您需要在其他地方處理資料,您可以更改此方法以僅將資料收集到單個佇列或類似佇列。
uj5u.com熱心網友回復:
在閱讀了更多有關 PS 主機的資訊后,我找到了這樣做的方法!基本上可以使用此行將所有其他流重定向到標準輸出流:
instance.Commands.Commands[0].MergeMyResults(PipelineResultTypes.All,PipelineResultTypes.Output);
這樣,instance.Invoke()回傳所有內容,包括詳細資訊、資訊等流。
然后通過檢查每個PSObject是什么,我可以根據物件的型別對它們進行不同的處理。完整示例:
using System.Management.Automation;
using System.Management.Automation.Runspaces;
var runSpace = RunspaceFactory.CreateRunspace(InitialSessionState.CreateDefault());
runSpace.Open();
var instance = PowerShell.Create(runSpace);
instance.AddScript("""
$VerbosePreference = 'Continue'
Write-Verbose "Line 1"
Write-Output "Line 2"
Write-Error "Line 3"
Write-Verbose "Line 4"
Write-Information "Line 5"
Write-Information "Line 6"
Write-Verbose "Line 7"
"""
);
instance.Commands.Commands[0].MergeMyResults(PipelineResultTypes.All,PipelineResultTypes.Output);
var output = instance.Invoke();
foreach (var o in output)
{
switch (o.ImmediateBaseObject)
{
case ErrorRecord er:
Console.WriteLine($"[ERR]: {er}");
break;
case WarningRecord wr:
Console.WriteLine($"[WRN]: {wr}");
break;
case VerboseRecord vr:
Console.WriteLine($"[VER]: {vr}");
break;
case InformationRecord ir:
Console.WriteLine($"[INF]: {ir}");
break;
case DebugRecord dr:
Console.WriteLine($"[DBG]: {dr}");
break;
default:
// I assume everything that ends up here is uncaptured output.
Console.WriteLine($"[OUT]: {o}");
break;
}
}
現在輸出的順序是正確的:
[VER]: Line 1
[OUT]: Line 2
[ERR]: Line 3
[VER]: Line 4
[INF]: Line 5
[INF]: Line 6
[VER]: Line 7
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/535813.html
