Suggested new function for System.Process - robustReadProcessWithExitCode

Andrew Miller andrew at
Fri Dec 17 05:09:27 CET 2010


I include my proposal for a new function, for System.Process , 
robustReadProcessWithExitCode, below.

Best wishes,
Andrew Miller

Motivation for change: using readProcessWithExitCode risks terminating 
the program due to uncaught IOException (ResourceExhausted) if the input 
string is not read entirely by the new process before the new process 
exits, because readProcessWithExitCode will attempt to write to a 
process that doesn't exist.

Often, it is the exit code and standard input and standard error that 
matters to a programmer, and not whether a process read all the input. 
There is therefore a case for a function like readProcessWithExitCode 
which is robust to the new process exiting before all input is read.

Suggested implementation:
{- |
robustReadProcessWithExitCode creates an external process, reads its
standard output and standard error strictly, waits until the process
terminates, and then returns the 'ExitCode' of the process,
the standard output, and the standard error.

'robustReadProcessWithExitCode' is a fairly simple wrapper
around 'createProcess'.  Constructing variants of these functions is
quite easy: follow the link to the source code to see how
'robustReadProcessWithExitCode' is implemented.

'robustReadProcessWithExitCode' differs from 'readProcessWithExitCode' 
it ignores IOExceptions encountered when writing the input to the new 
This can be useful if you don't know if the process reads all your 
input, but
you want to get the output and exit code even if it exits before reading all

     :: FilePath                 -- ^ command to run
     -> [String]                 -- ^ any arguments
     -> String                   -- ^ standard input
     -> IO (ExitCode,String,String) -- ^ exitcode, stdout, stderr
robustReadProcessWithExitCode cmd args input = do
     (Just inh, Just outh, Just errh, pid) <-
         createProcess (proc cmd args){ std_in  = CreatePipe,
                                        std_out = CreatePipe,
                                        std_err = CreatePipe }

     outMVar <- newEmptyMVar

     -- fork off a thread to start consuming stdout
     out  <- hGetContents outh
     _ <- forkIO $ C.evaluate (length out) >> putMVar outMVar ()

     -- fork off a thread to start consuming stderr
     err  <- hGetContents errh
     _ <- forkIO $ C.evaluate (length err) >> putMVar outMVar ()

     -- now write and flush any input, ignoring errors
     when (not (null input)) $ C.handle ((\e -> return ()) :: 
(C.IOException -> IO ())) $
       do hPutStr inh input; hFlush inh
     hClose inh -- done with stdin

     -- wait on the output
     takeMVar outMVar
     takeMVar outMVar
     hClose outh

     -- wait on the process
     ex <- waitForProcess pid

     return (ex, out, err)

More information about the Libraries mailing list