2009-11-24 16 views
5

Próbuję przekierować stdin i stdout do aplikacji konsolowej, dzięki czemu mogę komunikować się z nimi za pośrednictwem F #. Jednak w zależności od aplikacji konsoli oczywisty kod wydaje się nie udać. Poniższy F # Kod działa na dir ale nie powiedzie się (zawiesza się) na python i fsi:Przekierowywanie stdin i stdout do .Net

open System 
open System.Diagnostics 

let f = new Process() 
f.StartInfo.FileName <- "python" 
f.StartInfo.UseShellExecute <- false 
f.StartInfo.RedirectStandardError <- true 
f.StartInfo.RedirectStandardInput <- true 
f.StartInfo.RedirectStandardOutput <- true 
f.EnableRaisingEvents <- true 
f.StartInfo.CreateNoWindow <- true 
f.Start() 
let line = f.StandardOutput.ReadLine() 

To wisi na pytona ale pracuje dla reż.

Czy to ma zrobić z python i fsi za pomocą readline lub czy robię oczywisty błąd? Czy jest jakieś obejście, które pozwoliłoby mi na interakcję z FSI lub Pythonem REPL z F #?

Odpowiedz

3

To jest kod, którego szukasz (co ja wygodnie napisałem w rozdziale 9, Skrypty;) Jak wspomniano wcześniej, ReadLine blokuje, dopóki nie ma pełnej linii, która prowadzi do różnego rodzaju zawieszeń. Najlepiej jest podłączyć się do zdarzenia OutputDataRecieved.

open System.Text 
open System.Diagnostics 

let shellEx program args = 

    let startInfo = new ProcessStartInfo() 
    startInfo.FileName <- program 
    startInfo.Arguments <- args 
    startInfo.UseShellExecute <- false 

    startInfo.RedirectStandardOutput <- true 
    startInfo.RedirectStandardInput <- true 

    let proc = new Process() 
    proc.EnableRaisingEvents <- true 

    let driverOutput = new StringBuilder() 
    proc.OutputDataReceived.AddHandler(
     DataReceivedEventHandler(
      (fun sender args -> driverOutput.Append(args.Data) |> ignore) 
     ) 
    ) 

    proc.StartInfo <- startInfo 
    proc.Start() |> ignore 
    proc.BeginOutputReadLine() 

    // Now we can write to the program 
    proc.StandardInput.WriteLine("let x = 1;;") 
    proc.StandardInput.WriteLine("x + x + x;;") 
    proc.StandardInput.WriteLine("#q;;") 

    proc.WaitForExit() 
    (proc.ExitCode, driverOutput.ToString()) 

Output (co może stać się prettied górę):

val it : int * string = 
    (0, 
    "Microsoft F# Interactive, (c) Microsoft Corporation, All Rights ReservedF# Version 1.9.7.8, compiling for .NET Framework Version v2.0.50727For help type #help;;> val x : int = 1> val it : int = 3> ") 
+0

To jest doskonały, z wyjątkiem jestem nadal błahy wokół próbuje dowiedzieć się, jak znaleźć to gdzie każde wyjście polecenia zaczyna i kończy. Dzielenie> nie wydaje się być bardzo solidne. Próbowałem przekazać "--fsi-server: test" jako argument i wydaje mi się, że nie otrzymuję "SERVER-PROMPT>". Jakieś pomysły? – Tristan

+0

Niestety nie ma sposobu, aby dowiedzieć się w FSI, czy polecenie zostało wykonane, wykonując polecenie inne niż oczekiwanie na wyświetlenie ">", co oczywiście nie jest tak silne. (To staje się jeszcze bardziej skomplikowane, gdy spawnujesz asynchroniczne zadania, które przeżyją wykonanie zadania.) Do automatyzacji używamy następującej heurystyki: czekaj na "\ r \ n>" i nie zapisuj nowych wyników przez sekundę lub dłużej. –

+0

Dzięki. fsiserver.fs wydaje się działać przez IPC, ale nie jest oczywiste, jak go używać, tak jak powłoki VS. – Tristan

2

Założę się, że ani python, ani fsi nie są w rzeczywistości generowania linii tekstu do odczytania. Połączenie z numerem ReadLine zostanie zablokowane, dopóki nie będzie dostępna pełna linia kończąca się znakiem powrotu karetki lub podajnika linii.

Spróbuj odczytać znak na raz (z Read zamiast ReadLine) i zobacz, co się stanie.

2

To prawdopodobnie jest to, co mówi Michael Petrotta. Jeśli tak, nawet czytanie postaci nie pomoże. Musisz użyć wersji asynchronicznych (BeginOutputReadLine), aby Twoja aplikacja nie blokowała.

Powiązane problemy