
So I stared at the documentation in Control-Concurrent, learned about finally and MVar variables, and crossed the genes from the suggestions here to come up with runCommand :: String -> String -> IO (String,Bool) runCommand cmd input = do (inp,out,err,pid) <- runInteractiveCommand cmd let get h = do mvar <- newEmptyMVar let put xs = seq (length xs) (putMVar mvar xs) forkIO $ finally (hGetContents h >>= put) (put []) takeMVar mvar if null input then return () else hPutStr inp input output <- get out errmsg <- get err exit <- waitForProcess pid case exit of ExitSuccess -> return (output,True) ExitFailure _ -> do hPutStrLn stderr errmsg return (errmsg,False) which seems to work well; I haven't beat on it. I like the return type for my needs, e.g. I can write (out,ok) <- runCommand mark doc if ok then write out src else hPutStr stderr out So why don't the MVar examples in this thread bracket somehow, e.g. with finally as Control-Concurrent suggests: Note that we use finally from the Control.Exception module to make sure that the MVar is written to even if the thread dies or is killed for some reason. It seems to me that this could happen, with waitForProcess doing fine, yet the MVar never getting written. (I haven't written a test example to exercise this.)