haskellfunctional-programmingiopipe

Why can't print text passed via std input to createProcess-ed process?


Looking for usage examples of what's in System.Process, I landed at this page.

Here's an excerpt of the code I found there:

import System.IO
import System.Process

main = do
    (Just hin, Just hout, _, _) <- createProcess (proc "grep" ["hello"])
                                    { std_in = CreatePipe, std_out = CreatePipe }
    hPutStr hin "hello grep\ngoodbye grep"
    grepBytes <- hGetContents hout
    putStrLn "> grep hello"
    putStrLn grepBytes

I don't really understand why the execution blocks at the last line (indeed, if I change it to putStrLn "1" >> putStrLn grepBytes >> putStrLn "2", only 1 is printed). The code looks like I would have expected, so I'm a bit puzzled. Any clue?


Solution

  • Execution blocks because grep blocks, and grep blocks because execution blocks. Whoops!

    grep reads to the end of its file (even if that file is stdin). Since you haven't closed hin, grep can never be sure whether you were intending to send more later or not. A similar commentary applies to hGetContents: it reads to the end of its file, and since grep never ends its file, it can never be sure whether grep intended to output more later or not.

    Toss an hClose hin anywhere after your hPutStr but before the use of grepBytes to tell grep you're done. Then grep will return the favor and let hGetContents know it's done.

    You may ask: how come it worked for the other fellow? Well, I can't be sure, since I can't reproduce it. But one thing that could have happened is this: because hin is not mentioned after the hPutStr, it becomes garbage at that point, and when GHC collects it, it will graciously close it for you first if you forgot. So perhaps for the other fellow, garbage collection ran between the hPutStr and the use of grepBytes, closing hin as is needed. I would definitely say it is good practice not to rely on this behavior, and hClose each handle when you know you're done with it. (Corollary: don't use hGetContents, which also delays hClose to an undisclosed future time.)