我正在尝试从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()作为一个整体返回了整个过程的成功,而不仅仅是'开始',因此我看不出 的工作方式。
答案 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#式反应事件处理。