Хаскелл:Как передать содержимое одного дескриптора в другой в режиме реального времени
Вопрос
Я пишу программу, которая запускает внешний подпроцесс в интерактивном режиме, и мне нужно, чтобы содержимое дескриптора вывода выводилось на стандартный вывод, как только оно станет доступно.Я пробовал что-то вроде этого:
main = do processInfo <- createProcess (proc "ghci" []){std_out = CreatePipe,
std_in = CreatePipe }
case processInfo of
(Just hIn, Just hOut, _, _) -> do mainloop hIn hOut
hClose hIn
hClose hOut
_ -> do error "Unable to start process"
mainloop :: Handle -> Handle -> IO ()
mainloop inh outh =
do ineof <- hIsEOF outh
if ineof
then return ()
else do inpStr <- hGetLine outh
putStrLn inpStr
mainloop inh outh
Но это не работает, поскольку он распознает вывод только построчно, поэтому любой вывод в дескрипторе вывода процесса, который не завершается символом новой строки, не отображается.Я пробовал то же самое с hGetContents, но результат тот же.Я прочитал документацию как по System.Process, так и по System.IO и не нашел ничего убедительного.
Решение
hSetBuffering
это то, что вы ищете, по умолчанию (по крайней мере, в Unix) используется буферизация строк.Используйте его на стандартном вводе перед запуском основного цикла.
hSetBuffering hIn NoBuffering
и, при необходимости, также в дескрипторе вывода, если вы хотите сразу увидеть результаты на стороне вывода.Однако обратите внимание, что отключение буферизации может существенно снизить производительность.
Другие советы
Я новичок в Haskell, но помню, что недавно наткнулся на пример посимвольной обработки входных данных.Является hSetBuffering возможно, то, что вы ищете?
Буферизация — это одно, но вы также используете gGetLine
который будет ждать целой строки (или конца файла).Рассмотрите возможность использования hGetChar
если вы действительно хотите читать по одному символу за раз.И кстати, еще один способ обойти буферизацию — использовать hFlush
.