2009-11-24 2 views
5

Я пытаюсь перенаправить stdin и stdout консольного приложения, чтобы я мог взаимодействовать с ними через F #. Однако в зависимости от консольного приложения очевидный код, похоже, терпит неудачу. Следующий F # код работает для dir, но не (зависает) для python и fsi:Перенаправление stdin и stdout в .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() 

Это висит питона, но работает реж.

Это связано с использованием python и fsi с использованием readline или я делаю очевидную ошибку? Есть ли работа, которая позволила бы мне взаимодействовать с fsi или python REPL из F #?

ответ

3

Это код, который вы ищете (который я написал в главе 9, сценарии;) Как уже упоминалось ранее, ReadLine блокируется до тех пор, пока не будет полная линия, которая приведет к разным видам. Лучше всего подключиться к событию 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()) 

выход (который может выдержать быть 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

Это идеальный вариант, за исключением того я все еще возился вокруг, пытаясь выяснить, как найти то, что где каждый выход команды начинается и заканчивается. Разделение на> не кажется очень надежным. Я пробовал передавать «-fsi-server: test» в качестве аргумента, и, похоже, я не получаю «SERVER-PROMPT>». Есть идеи? – Tristan

+0

К сожалению, в FSI нет возможности узнать, выполняется ли команда, отличная от ожидающей отображения '>', что явно не является надежным. (Это становится еще сложнее при нерестах асинхронных задач, которые переживают выполнение задачи.) Для автоматизации мы используем следующую эвристику: wait for "\ r \ n>" && никакой новый вывод не был написан для второй или более. –

+0

Спасибо. fsiserver.fs, похоже, работает над IPC, но неясно, как использовать его, как оболочку VS. – Tristan

2

Держу пари, ни питон, ни fsi на самом деле генерируют строку текста для чтения. Вызов ReadLine будет заблокирован до тех пор, пока не будет доступна полная строка, заканчивающаяся возвратом каретки или переводом строки.

Попробуйте прочитать символ за раз (с Read вместо ReadLine) и посмотреть, что произойдет.

2

Возможно, это то, что говорит Майкл Петротта. Если это так, даже чтение персонажа не поможет. Что вам нужно сделать, так это использовать асинхронные версии (BeginOutputReadLine), чтобы ваше приложение не блокировалось.

Смежные вопросы