假设我想编写一个调用另一个程序的程序,该程序的输出同时包含 stdout
输出和 stderr
输出。
例如,我调用的这个程序是 F# 编译器试图编译包含错误的 F# 文件:
F# Compiler for F# 4.0 (Open Source Edition)
Freely distributed under the Apache 2.0 Open Source License
/builds/someLib.fs(27,42): error FS0001: Type mismatch. Expecting a
string * string
but given a
string * string * 'a
The tuples have differing lengths of 2 and 3
(前两行打印到stdout
,其余的打印到stderr
。)
所以我写了一个这样处理进程类的程序:
type OutChunk = StdOut of string | StdErr of string
type OutputBuffer = list<OutChunk>
type ProcessResult = { ExitCode: int; Output: OutputBuffer }
module Process =
let Execute (command: string, args: string, hidden: bool)
: ProcessResult =
// I know, this shit below is mutable, but it's a consequence of dealing with .NET's Process class' events
let outputBuffer = new System.Collections.Generic.List<OutChunk>()
let outputBufferLock = new Object()
use outWaitHandle = new AutoResetEvent(false)
use errWaitHandle = new AutoResetEvent(false)
let startInfo = new ProcessStartInfo(command, args)
startInfo.UseShellExecute <- false
startInfo.RedirectStandardOutput <- true
startInfo.RedirectStandardError <- true
use proc = new System.Diagnostics.Process()
proc.StartInfo <- startInfo
let outReceived (e: DataReceivedEventArgs): unit =
if (e.Data = null) then
outWaitHandle.Set() |> ignore
else
if not (hidden) then
Console.WriteLine(e.Data)
lock outputBufferLock (fun _ -> outputBuffer.Add(OutChunk.StdOut(e.Data)))
let errReceived (e: DataReceivedEventArgs): unit =
if (e.Data = null) then
errWaitHandle.Set() |> ignore
else
if not (hidden) then
Console.Error.WriteLine(e.Data)
lock outputBufferLock (fun _ -> outputBuffer.Add(OutChunk.StdErr(e.Data)))
proc.OutputDataReceived.Add outReceived
proc.ErrorDataReceived.Add errReceived
proc.Start() |> ignore
let exitCode =
try
proc.BeginOutputReadLine()
proc.BeginErrorReadLine()
proc.WaitForExit()
proc.ExitCode
finally
outWaitHandle.WaitOne() |> ignore
errWaitHandle.WaitOne() |> ignore
{ ExitCode = exitCode; Output = List.ofSeq(outputBuffer) }
let rec PrintToScreen (outputBuffer: OutputBuffer) =
match outputBuffer with
| [] -> ()
| head::tail ->
match head with
| StdOut(out) -> Console.WriteLine(out)
| StdErr(err) -> Console.Error.WriteLine(err)
PrintToScreen(tail)
但是,即使我在上面的代码中使用锁来防止在写入可变列表时出现竞争条件,有时当我运行调用 F# 编译器的 F# 程序时,然后调用 PrintToScreen
函数,我得到混合流:
F# Compiler for F# 4.0 (Open Source Edition)
/builds/someLib.fs(27,42): error FS0001: Type mismatch. Expecting a
Freely distributed under the Apache 2.0 Open Source License
string * string
but given a
string * string * 'a
The tuples have differing lengths of 2 and 3
(如您所见,许可证文本应该在编译器错误之前到达!但它没有)
这怎么可能?如何处理恶魔般的 System.Diagnostics.Process
类以正确的顺序接收流/事件?
最佳答案
这可能是输出缓冲的情况。
您是否尝试过在写入 stdout
和 stderr< 后调用
分别? Console.Out.Flush()
和 Console.Error.Flush()
/
关于.net - 如何避免以错误的顺序从 Process 类接收事件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40966435/