同步启动进程,并"输出"输出

Ben*_*jol 11 .net f# processstartinfo synchronous

我正在尝试从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
Run Code Online (Sandbox Code Playgroud)

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

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

UPDATE

我试过Brian的建议,它确实有效.

我的问题有点模糊.我的误解是,我假设的Process.Start()返回进程的成功作为一个整体,而不是仅仅的"开始",因此我看不出它如何工作.

Dmi*_*mov 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() }
Run Code Online (Sandbox Code Playgroud)

您还可以写下以下内容:

async {
    while true do
        let! args = Async.AwaitEvent p.OutputDataReceived
        ...
} |> Async.StartImmediate
Run Code Online (Sandbox Code Playgroud)

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

  • 我正在调用一个返回进程的静态Process.Start(ProcessStartInfo)(http://msdn.microsoft.com/en-us/library/0w4h05yb.aspx) (2认同)
  • 该解决方案的问题在于,只有在发送EOL之后才触发事件OutputDataReceived和ErrorDataReceived,因此可能会发生该过程要求用户在同一行中回答某些问题(例如,确定吗? [Y / N]`),然后F#进程包装程序将不会打印它 (2认同)