12

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() が「開始」だけでなく、プロセス全体の成功を返すと想定していたため、それがどのように機能するかわかりませんでした。

4

1 に答える 1

14

あなたが書いた形であなたが書いたコードは(ほぼ)OKです:process.Startあなたが指定したプロセスを別のプロセスで開始するので、出力ストリームの読み取りはプロセスの実行と並行して行われます。ただし、1 つの問題は、最後に 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# スタイルのリアクティブ イベント処理用。

于 2010-06-17T21:43:03.320 に答える