2010-06-17 5 views
10

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.Start는()는 부울을 반환 할 수 있습니다 어떻게 (내 경우에는 내가 자식 명령을 실행하기 위해 노력하고있어,하지만 그 질문에 접선) 점진적으로 출력을 꺼내기에는 충분히 비동기 적이어야합니다. 불행하게도

, 나는 현재 충분히 큰 저장소가없는 -

UPDATE ...

또는 가지에서 일어나고있는 어떤 순서로 말할 수 있도록 충분한 기계를 느리게 나는 브라이언의 노력 제안하고 작동합니다.

제 질문은 다소 모호했습니다. 내 오해는 Process.Start()가 'Start'뿐만 아니라 프로세스 전체의 성공을 반환했다는 것을 가정하고 따라서 일 수 있음을 알 수 없었습니다.

+1

비동기 워크 플로를 사용하여 다음과 같은 작업에 성공했습니다. http://stackoverflow.com/questions/2649161/need-help-regarding-async-and-fsi – Stringer

+0

실제로 어떤 질문이 있는지 잘 모르겠습니다. 이리. 위의 코드가 작동하지 않습니까? 문제가 무엇입니까? –

+1

tester.exe를 작성하면 stdout과 flushes에 몇 줄을 쓰고 2 초를 기다리며 또 다른 두 줄을 씁니다.이 코드가 원하는대로 작동하는지 테스트 할 수 있습니까? – Brian

답변

12

작성한 형식으로 작성한 코드는 (거의) ok입니다. process.Start, 다른 프로세스에서 지정한 프로세스를 시작하십시오. 그러면 출력 스트림 읽기가 프로세스 실행과 동시에 이루어집니다. 한 가지 문제는 결국 process.WaitForExit에 대한 호출을 던져야한다는 것입니다. 출력 스트림이 닫혀 있다는 사실이 프로세스가 종료되었음을 의미하지는 않습니다.

그러나 프로세스의 stdout과 stderr를 모두 읽으려는 경우 synchronyous 읽기 문제가 발생합니다. 2 개의 스트림을 동 기적으로 동시에 읽을 수있는 방법이 없습니다. stdout을 읽고 프로세스가에 쓰는 경우 교착 상태가됩니다. stderr를 실행하고 출력을 소비 할 때까지 기다립니다.

이 중재하려면 다음과 같이 OutputDataRecieved 및 ErrorDataRecieved에 가입 할 수 있습니다 : F 번호

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

반응성 이벤트 처리 스타일 :

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

또한의 라인을 따라 뭔가를 쓸 수 있습니다 .

+0

+1 나머지 질문에 대한 대답은 내가 묻지 않았다 :) 코드에서 단 하나의 오류는 Process.Start()가 프로세스를 반환하지 않는다는 것입니다.이 프로세스는 부울을 반환합니다. – Benjol

+2

정적 인 Process.Start (ProcessStartInfo) 프로세스를 반환합니다 (http://msdn.microsoft.com/en-us/library/0w4h05yb.aspx) –

+1

이 솔루션의 문제는 'OutputDataReceived' 이벤트를 호출하는 것입니다. 와'ErrorDataReceived'는 EOL이 보내진 후에 만 ​​발생합니다. 그래서 프로세스가 사용자에게 같은 줄에서 대답하도록 요청할 수 있습니다 (eg'확실합니까? [Y/N]') 그리고 나서 F # 프로세스 래퍼에 의해 출력되지 않을 것입니다 – knocte

관련 문제