Reputation: 5618
I'd like to have something like bash's 2>&1
redirect in Haskell that combines stdout
and stderr
from a process into a single Handle
. It would be nice to do this directly with System.Process.createProcess
or a similar library function, particularly if it used the same semantics as the bash redirect w.r.t. interleaving input from the handles.
The flexibility offered by createProcess
seems promising at first: one can specify a Handle
to use for the standard file descriptors, so the same Handle
could be given for both stdout
and stderr
. However, the Handle
arguments must already exist before the call. Without the ability to create a Handle
from thin air before calling the function, I'm not sure the problem can be solved this way.
Edit: The solution needs to work regardless of platform.
Upvotes: 15
Views: 961
Reputation: 25675
From here:
import GHC.IO.Handle -- yes, it's GHC-specific
import System.IO
main = do
stdout_excl <- hDuplicate stdout
hDuplicateTo stderr stdout -- redirect stdout to stderr
putStrLn "Hello stderr" -- will print to stderr
hPutStrLn stdout_excl "Hello stdout" -- prints to stdout
Upvotes: 4
Reputation: 15048
Since Windows supports pipe (3)
natively and GHC's IO library uses CRT file descriptors internally on Windows, it is possible to come up with a solution that works on both Windows and *nix at least. The basic algorithm is:
pipe
. This gives you two file descriptors which you can convert to Handles
with fdToHandle'
.createProcess
with stdout
and stderr
both set to the pipe's write end.The System.Posix.Internals
module, which exports c_pipe
, is hidden by default, but you can compile a custom version of base
that lets you access it (tip: use cabal-dev). Alternatively, you can access pipe
via FFI. NB: this solution is GHC-specific.
Upvotes: 2
Reputation: 152867
Getting your hands on a Handle
isn't too hard: System.IO
offers constants stdin,stdout,stderr :: Handle
and functions withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
and openFile :: FilePath -> IOMode -> IO Handle
.
Alternately, you could request new pipes from createProcess
and set yourself up as a forwarding service (reading from the new stdout
and stderr
handles of your child and sending both to wherever you like).
Upvotes: 4