Aneesh Durg
Aneesh Durg

Reputation: 464

Piping from stdin to process

I'm trying to pipe the stdin of my program to an external process using the following

import System.IO
import System.Posix.IO
import System.Posix.Process
import qualified System.Process as P
import Control.Concurrent (forkIO, killThread)
import Control.Monad

main :: IO ()
main = do
    h <- fdToHandle stdInput
    (Just hIn, _, _, p) <-
        P.createProcess (P.shell "runhaskell echo.hs"){ P.std_in = P.CreatePipe }

    hSetBuffering hIn NoBuffering
    tid <- forkIO $ getInput hIn
    e <- P.waitForProcess p
    killThread tid
    print e

getInput hin = do  
    forever $ do 
        l <- getLine
        hPutStrLn hin l

where echo.hs just echoes stdin to stdout, but if I wait a couple seconds between giving new input, I get the following error:

pipes.hs: <stdin>: hGetLine: invalid argument (Bad file descriptor)

when I tried compiling with ghc pipes.hs, the compiled program would not redirect stdin to the stdin of echo.hs at all

Upvotes: 2

Views: 747

Answers (1)

Michael Snoyman
Michael Snoyman

Reputation: 31315

Your fdToHandler stdInput call creates a new Handle pointing at file descriptor 0 (stdin) of the original process. After a bit of time, the garbage collector notices that it's no longer being used, and garbage collects the Handle, which in turn causes the underlying file descriptor to be closed. Then your getLine (which uses System.IO.stdin) call fails. That's because that Handle is still open, but the underlying file descriptor it's pointing at has been closed.

FWIW, I'd recommend using binary I/O on the handles to avoid issues with character encodings.

Upvotes: 4

Related Questions