在。net中重定向stdin和stdout

时间:2022-01-23 21:01:45

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:

我试图将stdin和stdout重定向到控制台应用程序中,以便通过f#与它们交互。然而,根据控制台应用程序的不同,明显的代码似乎会失败。下面的f#代码为dir工作,但对于python和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.

这挂在python上,但对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#?

这与使用readline的python和fsi有关吗?还是我犯了一个明显的错误?是否有一项工作可以让我与f#中的fsi或python REPL进行交互?

3 个解决方案

#1


3  

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.

这是您正在寻找的代码(我在第9章“脚本;如前所述,ReadLine将阻塞,直到有一条完整的行导致所有类型的挂起。最好的办法是加入到输出数据的事件中。

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> ")

#2


2  

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.

我敢打赌,python和fsi实际上都没有生成要读取的一行文本。对ReadLine的调用将被阻塞,直到以回车或换行符结尾的完整行可用为止。

Try reading a character at a time (with Read instead of ReadLine) and see what happens.

试着一次读一个字符(用Read代替ReadLine),看看会发生什么。

#3


2  

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.

这可能就是迈克尔·佩特洛塔所说的。如果是这样的话,即使读一个角色也无济于事。你需要做的是使用异步版本(BeginOutputReadLine),这样你的应用就不会被屏蔽。

#1


3  

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.

这是您正在寻找的代码(我在第9章“脚本;如前所述,ReadLine将阻塞,直到有一条完整的行导致所有类型的挂起。最好的办法是加入到输出数据的事件中。

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> ")

#2


2  

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.

我敢打赌,python和fsi实际上都没有生成要读取的一行文本。对ReadLine的调用将被阻塞,直到以回车或换行符结尾的完整行可用为止。

Try reading a character at a time (with Read instead of ReadLine) and see what happens.

试着一次读一个字符(用Read代替ReadLine),看看会发生什么。

#3


2  

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.

这可能就是迈克尔·佩特洛塔所说的。如果是这样的话,即使读一个角色也无济于事。你需要做的是使用异步版本(BeginOutputReadLine),这样你的应用就不会被屏蔽。