同步启动进程,并“输出”输出

时间:2010-06-17 20:30:46

标签: .net f# synchronous processstartinfo

我正在尝试从F#开始一个进程,等到它完成,但也要逐步读取它的输出。

这是正确/最好的方法吗? (在我的情况下,我正在尝试执行git命令,但这与问题相关)

let gitexecute (logger:string->unit) cmd = 
    let procStartInfo = new ProcessStartInfo(@"C:\Program Files\Git\bin\git.exe", cmd) 

    // Redirect to the Process.StandardOutput StreamReader.
    procStartInfo.RedirectStandardOutput <- true
    procStartInfo.UseShellExecute <- false;

    // Do not create the black window.
    procStartInfo.CreateNoWindow <- true;

    // Create a process, assign its ProcessStartInfo and start it
    let proc = new Process();
    proc.StartInfo <- procStartInfo;
    proc.Start() |> ignore

    // Get the output into a string
    while not proc.StandardOutput.EndOfStream do
        proc.StandardOutput.ReadLine() |> logger

我不明白的是proc.Start()如何返回一个布尔值,并且也足以让我逐步获得输出。

不幸的是,我目前没有足够大的存储库 - 或者说机器速度慢,能够分辨出事情发生的顺序......

更新

我尝试了Brian的建议,它确实有效。

我的问题有点模糊。我的误解是我假设Process.Start()作为一个整体返回了整个过程的成功,而不仅仅是'开始',因此我看不出 的工作方式。

1 个答案:

答案 0 :(得分:14)

您在编写的表单中编写的代码(几乎)是正确的:process.Start启动您指定的进程,以及另一个进程,因此您的输出流读取将与您的流程执行并行执行。但有一个问题是你最后应该调用process.WaitForExit - 输出流关闭的事实并不意味着进程终止。

但是,如果你尝试读取进程的stdout和stderr,你将遇到同步读取的问题:无法同步和同时读取2个流 - 如果你读取stdout并且进程正在写入stderr,你将会死锁并等待你消耗其输出,反之亦然。

要调解此信息,您可以订阅OutputDataRecieved和ErrorDataRecieved,如下所示:

type ProcessResult = { exitCode : int; stdout : string; stderr : string }

let executeProcess (exe,cmdline) =
    let psi = new System.Diagnostics.ProcessStartInfo(exe,cmdline) 
    psi.UseShellExecute <- false
    psi.RedirectStandardOutput <- true
    psi.RedirectStandardError <- true
    psi.CreateNoWindow <- true        
    let p = System.Diagnostics.Process.Start(psi) 
    let output = new System.Text.StringBuilder()
    let error = new System.Text.StringBuilder()
    p.OutputDataReceived.Add(fun args -> output.Append(args.Data) |> ignore)
    p.ErrorDataReceived.Add(fun args -> error.Append(args.Data) |> ignore)
    p.BeginErrorReadLine()
    p.BeginOutputReadLine()
    p.WaitForExit()
    { exitCode = p.ExitCode; stdout = output.ToString(); stderr = error.ToString() }

您还可以写下以下内容:

async {
    while true do
        let! args = Async.AwaitEvent p.OutputDataReceived
        ...
} |> Async.StartImmediate

用于F#式反应事件处理。