2010-06-17 6 views
10

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

+1

У меня был некоторый успех с использованием асинхронных рабочих процессов для этой задачи: http://stackoverflow.com/questions/2649161/need-help-regarding-async-and-fsi – Stringer

+0

На самом деле я не уверен, что вопрос Вот. Работает ли ваш код выше или нет? В чем проблема? –

+1

Мне кажется, вы можете написать tester.exe, который пишет пару строк в stdout и flushes, ждет две секунды, пишет еще две строки, ... и использовать это, чтобы проверить, ведет ли этот код, как вы хотите, да? – Brian

ответ

12

Код, который вы написали, в форме, которую вы написали (почти) ok: 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 # -Style реактивных обработок событий ,

+0

+1 для ответа на оставшуюся часть вопроса, о котором я не спрашивал :). Только одна ошибка в вашем коде заключается в том, что Process.Start() не возвращает процесс, он возвращает логическое значение. – Benjol

+2

Я вызываю статический Process.Start (ProcessStartInfo), который возвращает процесс (http://msdn.microsoft.com/en-us/library/0w4h05yb.aspx) –

+1

Проблема с этим решением заключается в том, что события 'OutputDataReceived' и «ErrorDataReceived» срабатывает только после отправки EOL, так что может случиться так, что процесс запрашивает у пользователя ответ в той же строке (например,'Ты уверен? [Y/N] '), а затем он не будет распечатан с помощью обертки процесса F # – knocte