12 Stimmen

Synchroner Start eines Prozesses und "Streaming" der Ausgabe

Ich versuche, einen Prozess von F# aus zu starten, zu warten, bis er fertig ist, aber auch seine Ausgabe progressiv zu lesen.

Ist dies die richtige/beste Vorgehensweise? (In meinem Fall versuche ich, Git-Befehle auszuführen, aber das hat mit der Frage nichts zu tun)

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() einen Boolean zurückgeben kann und auch asynchron genug für mich, um die Ausgabe aus der while progressiv zu erhalten.

Leider verfüge ich derzeit nicht über ein ausreichend großes Repository - oder einen ausreichend langsamen Rechner -, um feststellen zu können, in welcher Reihenfolge die Dinge ablaufen...

UPDATE

Ich habe den Vorschlag von Brian ausprobiert, und es funktioniert.

Meine Frage war etwas vage. Mein Missverständnis bestand darin, dass ich davon ausging, dass Process.Start() den Erfolg des Prozesses als Ganzes und nicht nur den des "Starts" zurückgibt, und daher konnte ich nicht sehen, wie es könnte travail.

14voto

Dmitry Lomov Punkte 1406

Der Code, den Sie in der Form geschrieben haben, in der Sie ihn geschrieben haben, ist (fast) in Ordnung: process.Start startet den Prozess, den Sie angeben, in, nun ja, einem anderen Prozess, so dass Ihr Ausgabestrom parallel zur Prozessausführung gelesen wird. Ein Problem ist jedoch, dass Sie am Ende einen Aufruf an process.WaitForExit einfügen sollten - die Tatsache, dass der Ausgabestrom geschlossen ist, bedeutet nicht, dass der Prozess beendet wurde.

Sie werden jedoch Probleme mit dem synchronen Lesen bekommen, wenn Sie versuchen, sowohl stdout als auch stderr des Prozesses zu lesen: es gibt keine Möglichkeit, 2 Ströme synchron und gleichzeitig zu lesen - Sie werden in eine Sackgasse geraten, wenn Sie stdout lesen und der Prozess auf stderr schreibt und darauf wartet, dass Sie seine Ausgabe konsumieren oder umgekehrt.

Um dies zu vermitteln, können Sie OutputDataRecieved und ErrorDataRecieved wie folgt 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 in der Art von schreiben:

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

für die reaktive Ereignisbehandlung im F#-Stil.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X