改进的答案 - 只要您使用Start-Job
而不是Start-Process
没有问题
结果发现,当脚本运行时,STDOUT和STDERR会累积在字符串数组$job.ChildJobs[0].Output
和$job.ChildJobs[0].Error
中。因此,您可以定期轮询这些值并将它们写出来。可能有点hack,但它有效。
它不是一个流,所以您必须手动跟踪数组中的起始索引。
这段代码比我的原始答案更简单,最后您将在$job.ChildJobs[0].Output
中得到整个STDOUT。作为这个演示的额外奖励,调用脚本是PS7,后台作业是PS5。
$scriptBlock = {
Param ([int]$param1, [int]$param2)
$PSVersionTable
Start-Sleep -Seconds 1
$param1 + $param2
}
$parameters = @{
ScriptBlock = $scriptBlock
ArgumentList = 1, 2
PSVersion = 5.1
}
$timeoutSec = 5
$job = Start-Job @parameters
$job.ChildJobs[0].Output
$index = $job.ChildJobs[0].Output.Count
while ($job.JobStateInfo.State -eq [System.Management.Automation.JobState]::Running) {
Start-Sleep -Milliseconds 200
$job.ChildJobs[0].Output[$index]
$index = $job.ChildJobs[0].Output.Count
if (([DateTime]::Now - $job.PSBeginTime).TotalSeconds -gt $timeoutSec) {
throw "Job timed out."
}
}
正如指出的那样,我的原始答案可以交错输出。这是PowerShell事件处理的限制。这是无法修复的问题。
原始答案,请勿使用 - 只是为了兴趣而留在这里
如果有超时,
ReadToEnd()
不是一个选项。你可以做一些花哨的循环,但在我看来,最“干净”的方法是忽略流。而是挂钩
OutputDataReceived
/
ErrorDataReceived
事件,收集输出。这种方法还避免了其他人提到的线程问题。
在C#中,这很简单,但在Powershell中却很棘手和冗长。特别是,由于某种原因,
add_OutputDataReceived
不可用。(至少在PowerShell 5.1中似乎是这样。)为了解决这个问题,您可以使用
Register-ObjectEvent
。
$stdout = New-Object System.Text.StringBuilder
$stderr = New-Object System.Text.StringBuilder
$proc = [System.Diagnostics.Process]@{
StartInfo = @{
FileName = 'ping.exe'
Arguments = 'google.com'
RedirectStandardOutput = $true
RedirectStandardError = $true
UseShellExecute = $false
WorkingDirectory = $PSScriptRoot
}
}
$stdoutEvent = Register-ObjectEvent $proc -EventName OutputDataReceived -MessageData $stdout -Action {
$Event.MessageData.AppendLine($Event.SourceEventArgs.Data)
}
$stderrEvent = Register-ObjectEvent $proc -EventName ErrorDataReceived -MessageData $stderr -Action {
$Event.MessageData.AppendLine($Event.SourceEventArgs.Data)
}
$proc.Start() | Out-Null
$proc.BeginOutputReadLine()
$proc.BeginErrorReadLine()
Wait-Process -Id $proc.Id -TimeoutSec 5
if ($proc.HasExited) {
$exitCode = $proc.ExitCode
}
else {
Stop-Process -Force -Id $proc.Id
$exitCode = -1
}
Unregister-Event $stdoutEvent.Id
Unregister-Event $stderrEvent.Id
Write-Output $stdout.ToString()
Write-Output $stderr.ToString()
Write-Output "Exit code: $exitCode"
- 所展示的代码是快乐路径(stderr为空)
- 要测试超时路径,请将
-TimeoutSec
设置为 .5
- 要测试失败路径(stderr有内容),请将
FileName
设置为 'cmd'
,并将 Arguments
设置为 /C asdf
$process= ping localhost
# 会将输出保存在进程变量中。 - mjsr