This should be a simple question, but so far I haven’t found any direct answer to it: how does one ignore the stderr
(or stdout
) of a Haskell process using the process
library? For instance, let’s say I have the following:
let proc = (shell "dir /z") {
std_in = UseHandle stdin
, std_out = CreatePipe
}
(_, out, _, rProc) <- createProcess Proc
exitCode <- waitForProcess rProc
(Side note: in Windows, I do know that dir
doesn’t have a /z
switch. That’s why I chose it — so I could get some interesting output on stderr
.)
Doing this just causes stderr
to be printed to the console. Now let’s say I want to ignore stderr
. How do I do this?
The only clue I have found is in this part of the process
documentation:
NoStream
Close the stream's file descriptor without passing a Handle. On POSIX systems this may lead to strange behavior in the child process because attempting to read or write after the file has been closed throws an error. This should only be used with child processes that don't use the file descriptor at all. If you wish to ignore the child process's output you should either create a pipe and drain it manually or pass aHandle
that writes to/dev/null
.
This is somewhat helpful, but still leaves some questions unanswered. On non-POSIX systems, is NoStream
OK to use? It refers to creating a pipe then draining it, but I can’t find any information on how to do this? And /dev/null
is NUL
on Windows, except when you’re using MSYS or Cygwin, when it’s /dev/null
again (I think) — so I want to avoid that.
So to reiterate my question: what is the recommended, OS-agnostic way to ignore the stderr
of a process?
Here is one correct way:
import Control.Concurrent
import Control.Exception
import System.IO
import System.Process
forceGetContents :: String -> IO ()
forceGetContents s = evaluate (length s) >> return ()
main = do
outMVar <- newEmptyMVar
let proc = (shell "dir /z") {
std_in = UseHandle stdin
, std_out = CreatePipe
, std_err = CreatePipe
}
(_, Just out, Just err, rProc) <- createProcess proc
forkIO (hGetContents err >>= forceGetContents)
forkIO $ do
s <- hGetContents out
forceGetContents s
putMVar outMVar s
exitCode <- waitForProcess rProc
outContents <- takeMVar outMVar
putStr outContents -- or whatever
Some things worth noting from comments on a deleted answer:
waitForProcess
, you should fork a thread to drain the output pipe. Otherwise it may be killed before it can print everything it wants to, giving incomplete output.forceGetContents
is a good way to force a String
returned by an hGetContents
to be fully evaluated, but there are other String
-producers which may need a more involved forcing function. See also rnf
from the deepseq
package.You can address both (2) and (3) if there is a protocol that you know the process will follow where you will know when it is done producing output. Then you can stream the output (reducing memory pressure for bits that can be discarded early), and can delay the waitForProcess
until you know it is done outputting (avoiding the need to fork a thread to drain the output -- though still requiring a forked thread for the errors!).