Подтвердить что ты не робот

Запуск процесса синхронно и "потоковой" вывод

Я ищу попытку начать процесс с 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() может возвращать логическое значение, а также быть достаточно асинхронным для того, чтобы я получал выход из этого процесса постепенно.

К сожалению, в настоящее время у меня нет достаточно большого хранилища - или достаточно медленной машины, чтобы иметь возможность рассказать, в каком порядке происходят события...

UPDATE

Я попробовал предложение Брайана, и он действительно работает.

Мой вопрос был немного расплывчатым. Мое недоразумение состояло в том, что я предположил, что Process.Start() вернул успех процесса в целом, а не только "Пуск", и поэтому я не мог понять, как он может работать.

4b9b3361

Ответ 1

Код, который вы написали в том виде, в котором вы его написали (почти) ok: process.Start запустите процесс, который вы указали, ну, а другой процесс, поэтому ваши выходные данные будут повторяться параллельно с выполнением вашего процесса. Одна проблема заключается в том, что вы должны бросить вызов process.WaitForExit в конце - тот факт, что выходной поток закрыт, не означает, что процесс завершен.

Однако вы столкнетесь с проблемами с синхронным чтением, если попытаетесь прочитать как stdout, так и stderr процесса: нет возможности для чтения двух потоков синхронно и одновременно - вы будете заторможены, если будете читать 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 #.