2010-06-17 6 views
10

Ich versuche zu versuchen, einen Prozess von F # zu starten, warte bis er fertig ist, aber lese ihn auch progressiv.Einen Prozess synchron starten und die Ausgabe "streamen"

Ist dies der richtige/beste Weg? (In meinem Fall ich versuche git Befehle auszuführen, aber das ist tangential zur Frage)

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 

Was ich nicht verstehe, ist, wie die proc.Start() eine boolean zurückgeben kann und auch asynchron genug für mich sein, um die Ausgabe schrittweise zu bekommen.

Leider habe ich momentan keine ausreichend große Repository haben - oder genug Maschine verlangsamen, in der Lage sein zu sagen, welche um die Dinge in geschehen ...

UPDATE

Ich habe versucht, Brians Vorschlag, und es funktioniert.

Meine Frage war ein bisschen vage. Mein Missverständnis war, dass ich annahm, dass Process.Start() den Erfolg des Prozesses als Ganzes zurückgab, und nicht nur vom "Start", und somit konnte ich nicht sehen, wie es funktionieren konnte.

+1

Ich hatte einige Erfolge mit Asynchronous Workflows für diese Art von Aufgabe: http://StackOverflow.com/Questions/2649161/Need-Help-Grenze-Async-and-Fsi – Stringer

+0

Ich bin eigentlich nicht sicher, was die Frage ist Hier. Funktioniert Ihr Code oben oder nicht? Was ist das Problem? –

+1

Es scheint mir, Sie könnten tester.exe schreiben, schreibt ein paar Zeilen an stdout und flushes, wartet zwei Sekunden, schreibt zwei weitere Zeilen, ... und benutzt das, um zu testen, ob sich dieser Code so verhält, wie Sie wollen, ja? – Brian

Antwort

12

Der Code, den Sie in der Form geschrieben haben, die Sie ihm geschrieben haben, ist (fast) ok: process.Start startet den Prozess, den Sie angeben, na ja, ein anderer Prozess, so dass Ihr Ausgabestream parallel zur Prozessausführung abläuft. Ein Problem ist jedoch, dass Sie am Ende einen Aufruf an process.WaitForExit einwerfen sollten - die Tatsache, dass der Ausgabestream geschlossen ist, impliziert nicht, dass der Prozess beendet wird.

Allerdings werden Sie Probleme mit synchrony lesen, wenn Sie versuchen, beide stdout und stderr des Prozesses zu lesen: Es gibt keine Möglichkeit, 2 Streams synchron und gleichzeitig zu lesen - Sie werden Deadlock, wenn Sie stdout lesen und Prozess schreibt stderr und wartet darauf, dass Sie seine Ausgabe konsumieren oder umgekehrt.

Um dies zu vermitteln, können Sie zu OutputDataRecieved und ErrorDataRecieved, wie dies abonnieren:

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() } 

Sie können auch etwas entlang der Linien von schreiben:

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

für F # -Stil reaktive Ereignisbehandlung .

+0

+1 für die Beantwortung des Rests der Frage, die ich nicht gefragt habe :) Nur ein Fehler in Ihrem Code ist, dass Process.Start() den Prozess nicht zurückgibt, gibt es einen booleschen Wert zurück. – Benjol

+2

Ich rufe eine statische Process.Start (ProcessStartInfo), die einen Prozess (http://msdn.microsoft.com/en-us/library/0w4h05yb.aspx) –

+1

zurückgibt das Problem mit dieser Lösung ist, dass die Ereignisse 'OutputDataReceived' und 'ErrorDataReceived' werden nur ausgelöst, nachdem ein EOL gesendet wurde. Daher kann es passieren, dass der Prozess etwas zur Antwort des Benutzers in derselben Zeile (z. B.Bist du sicher? [Y/N] ') und wird dann nicht vom F # -Prozesswrapper gedruckt – knocte