Запуск процесса синхронно и “потоковая передача” вывода

Я смотрю на попытку запустить процесс с F#, ожидать, пока это не закончено, но также и читайте, это прогрессивно производится.

Действительно ли это - право/лучший способ сделать это? (В моем случае я пытаюсь выполнить команды мерзавца, но это является тангенциальным к вопросу),

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

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

ОБНОВЛЕНИЕ

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

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

11
задан Benjol 18 June 2010 в 06:00
поделиться

1 ответ

Код, который вы написали в том виде, в котором вы его написали, (почти) хорош: 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#.

14
ответ дан 3 December 2019 в 08:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: