2009-11-24 12 views
5

Estoy tratando de redirigir stdin y stdout de una aplicación de consola, para que pueda interactuar con ellos a través de F #. Sin embargo, dependiendo de la aplicación de la consola, el código obvio parece fallar. El siguiente código C# funciona para dir pero falla (se bloquea) para python y fsi:Redireccionando stdin y stdout en .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() 

Este cuelga para Python, pero funciona para dir.

¿Esto tiene que ver con python y fsi usando readline o estoy cometiendo un error obvio? ¿Hay algún trabajo que me permita interactuar con fsi o python REPL desde F #?

Respuesta

3

Este es el código que está buscando (que convenientemente he escrito en el Capítulo 9, Secuencias de comandos;) Como se mencionó anteriormente, ReadLine bloquea hasta que haya una línea completa que conduce a todo tipo de bloqueos. Su mejor apuesta es conectar el evento 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()) 

de salida (que podía soportar estar prettied):

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

Esto es perfecto, excepto todavía estoy jugando alrededor tratando de encontrar la manera de encontrar lo que cada salida del comando comienza y termina. Dividir en> no parece muy robusto. He intentado pasar "--fsi-server: test" como argumento y parece que no recibo "SERVER-PROMPT>". ¿Algunas ideas? – Tristan

+0

Desgraciadamente, no hay manera de saber en FSI si se ejecuta un comando que no sea esperar a que se muestre '>', que obviamente no es tan robusto. (Esto se vuelve incluso más complejo cuando se generan tareas asíncronas que sobreviven a la ejecución de la tarea). Para la automatización utilizamos la siguiente heurística: esperar a "\ r \ n>" && no se ha escrito ninguna salida durante un segundo o más. –

+0

Gracias. fsiserver.fs parece funcionar sobre IPC, pero no es obvio cómo usarlo como el shell VS. – Tristan

2

Apuesto a que ni python ni fsi son en realidad generando una línea de texto para ser leída. La llamada al ReadLine se bloqueará hasta que esté disponible una línea completa que finalice con un retorno de carro o avance de línea.

Intenta leer un personaje a la vez (con Read en lugar de ReadLine) y mira qué pasa.

2

Probablemente sea lo que dice Michael Petrotta. Si ese es el caso, incluso leer un personaje no ayudará. Lo que debe hacer es usar las versiones asíncronas (BeginOutputReadLine) para que su aplicación no se bloquee.

Cuestiones relacionadas