代码之家  ›  专栏  ›  技术社区  ›  Benjol

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

  •  11
  • Benjol  · 技术社区  · 14 年前

    我正在尝试从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()如何返回一个布尔值,并且异步到足以让输出逐步退出。

    不幸的是,我目前没有足够大的存储库-或足够慢的机器,无法判断事情的发生顺序…

    更新

    我试过布赖恩的建议,但效果不错。

    我的问题有点含糊。我的误解是我假定process.start()返回了整个流程的成功,而不仅仅是“start”,因此我看不到它是如何实现的。 能够 工作。

    1 回复  |  直到 14 年前
        1
  •  14
  •   Dmitry Lomov    14 年前

    以您编写的形式编写的代码(几乎)可以:process。启动在另一个进程中指定的进程,这样,输出流读取将与进程执行并行进行。不过,有一个问题是,您最终应该引发对process.waitforexit的调用—输出流关闭并不意味着进程已终止。

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

    要调解此问题,您可以订阅OutputDataReceived和ErrorDataReceived,如下所示:

    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型反应性事件处理。

    推荐文章