I'm trying to redirect stdin and stdout of a console application, so that I can interact with them via F#. However, depending on the console application the obvious code seems to fail. The following F# code works for dir
but fails (hangs) for python
and fsi
:
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()
This hangs for python but works for dir.
Does this have do with python and fsi using readline or am I making an obvious mistake? Is there a work around that would allow me to interact with fsi or python REPL from F#?
This is the code you are looking for (which I conveniently have written in Chapter 9, Scripting ;) As mentioned earlier, ReadLine blocks until there is a full line which leads to all sorts of hangs. Your best bet is to hook into the OutputDataRecieved event.
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 (which could stand to be prettied up):
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> ")
I'll bet neither python nor fsi are actually generating a line of text to be read. The call to ReadLine
will block until a full line ending with a carriage return or linefeed is available.
Try reading a character at a time (with Read
instead of ReadLine
) and see what happens.
It's probably what Michael Petrotta says. If that's the case, even reading a character won't help. What you need to do is to use the asynchronous versions (BeginOutputReadLine) so that your app won't block.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With