我正在尝试了解接收工作的内部工作方式。在下面的代码中,我可以看到作业对象在哪里保留不同蒸汽的记录:
$InformationPreference = 'SilentlyContinue'
$sb = {
$VerbosePreference = 'Continue'
$InformationPreference = 'Continue'
$WarningPreference = 'Continue'
Write-Warning 'warning1'
Write-Information 'information1'
Write-Warning 'warning2'
Write-Information 'information2'
Write-Verbose 'verbose1'
Write-Information 'information3'
}
$job = Start-Job -ScriptBlock $sb | Wait-Job
# my messages are here:
$job.ChildJobs[0].Verbose.Count # prints 1
$job.ChildJobs[0].Information.Count # prints 3, only InformationRecord has TimeGenerated property
$job.ChildJobs[0].Warning.Count # prints 2
Receive-Job $job
# prints:
# WARNING: warning1
# information1
# WARNING: warning2
# information2
# VERBOSE: verbose1
# information3
但是,我如何写自己的"接收工作"版本并保留不同消息的原始顺序?我试图检查源代码,但没有太多意义:
https://github.com/powershell/powershell/powershell/blob/master/src/src/system.management.automation/engine/remoting/commands/receivejob.cs
private void WriteJobResults(Job job)
{
// ...
Collection<PSObject> output = ReadAll<PSObject>(job.Output);
foreach (PSObject o in output)
{
// ...
WriteObject(o);
}
Collection<ErrorRecord> errorRecords = ReadAll<ErrorRecord>(job.Error);
foreach (ErrorRecord e in errorRecords)
{
// ...
mshCommandRuntime.WriteError(e, true);
}
Collection<VerboseRecord> verboseRecords = ReadAll(job.Verbose);
foreach (VerboseRecord v in verboseRecords)
{
// ...
mshCommandRuntime.WriteVerbose(v, true);
}
// and so on for other streams...
}
@petseral指出,我引用了错误的代码。这是实际执行的方法:
// extract results and handle them
Collection<PSStreamObject> results = ReadAll<PSStreamObject>(job.Results);
if (_wait)
{
foreach (var psStreamObject in results)
{
psStreamObject.WriteStreamObject(this, job.Results.SourceId);
}
}
else
{
foreach (var psStreamObject in results)
{
psStreamObject.WriteStreamObject(this);
}
}
不幸的是,Results
是内部属性。我研究了它的工作原理,以下是代码:
$nonPublicInstance = [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Instance
$JobResultsProperty = $job.GetType().GetProperty('Results', $nonPublicInstance)
$results = $JobResultsProperty.GetValue($job.ChildJobs[0])
$PSStreamObjectValueProperty = [System.Management.Automation.Remoting.Internal.PSStreamObject].GetProperty('Value', $nonPublicInstance)
foreach ($result in $results)
{
$value = $PSStreamObjectValueProperty.GetValue($result)
switch ($result.ObjectType)
{
'Verbose' {
Write-Output "Verbose Record: $value"
}
'Information' {
Write-Output "Information Record: $value"
}
'WarningRecord' {
Write-Output "Warning Record: $value"
}
'MethodExecutor' {
Write-Output "MethodExecutor"
}
}
}
由于某种原因,ObjectType=Verbose
不在收集中。我假设从MethodExecutor
记录中提取详细记录。输出:
MethodExecutor
Warning Record: warning1
MethodExecutor
Information Record: information1
MethodExecutor
Warning Record: warning2
MethodExecutor
Information Record: information2
MethodExecutor
MethodExecutor
Information Record: information3
MethodExecutor
我找到了一种更好的方法,如何从工作中获得结果,但是它只能与新创建的作业一起使用。下面的代码在集合上添加了DataAdded
事件的事件处理程序:
$job = Start-Job -ScriptBlock $sb
$records = New-Object 'System.Collections.Generic.List[System.String]'
Register-ObjectEvent -InputObject $job.ChildJobs[0].Verbose -EventName 'DataAdded' -Action { $records.Add('verbose: ' + $Sender[$EventArgs.Index]) }.GetNewClosure() | Out-Null
Register-ObjectEvent -InputObject $job.ChildJobs[0].Information -EventName 'DataAdded' -Action { $records.Add('information: ' + $Sender[$EventArgs.Index]) }.GetNewClosure() | Out-Null
Register-ObjectEvent -InputObject $job.ChildJobs[0].Warning -EventName 'DataAdded' -Action { $records.Add('warning: ' + $Sender[$EventArgs.Index]) }.GetNewClosure() | Out-Null
Wait-Job $job | Out-Null
$records | Format-List
输出:
warning: warning1
information: information1
warning: warning2
information: information2
verbose: verbose1
information: information3