Haskell: Cómo tubería el contenido de un asa en otro en tiempo real
Pregunta
Estoy escribiendo un programa que se ejecuta un proceso de sub externa de forma interactiva y necesito el contenido de la manija de salida a ser la salida a la salida estándar tan pronto como esté disponible. He intentado algo como esto:
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
Pero esto no funciona, ya que sólo reconoce la línea de salida por línea, por lo que cualquier salida en el mango de salida procesos que no está terminada por un salto de línea no aparece. He intentado hacer lo mismo con hGetContents pero produce el mismo resultado. He leído a través de la documentación de ambos System.Process y System.IO y realmente no he encontrado nada concluyente.
Solución
hSetBuffering
es lo que estás buscando, el valor por defecto (en Unix al menos) es el búfer de línea. Usarlo en la entrada estándar antes de empezar el bucle principal
hSetBuffering hIn NoBuffering
y opcionalmente también en el mango de salida si desea ver los resultados inmediatamente en el lado de salida. Tenga en cuenta, sin embargo, que la desactivación de amortiguación puede reducir de manera drástica el rendimiento.
Otros consejos
Estoy en gran medida un novato Haskell, pero recuerdo que viene a través de un ejemplo reciente de procesar la entrada de caracteres carácter por. Es hSetBuffering posiblemente lo que está buscando?
Buffering es una cosa, pero también se están utilizando gGetLine
que esperar a una línea entera (o al final del archivo). Considere el uso de hGetChar
si realmente desea leer un carácter a la vez.
Y por cierto, otra manera de superar el búfer es utilizar hFlush
.