开发者

How can I combine Handles in Haskell?

开发者 https://www.devze.com 2023-03-21 07:22 出处:网络
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.c

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开发者_JAVA百科 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.


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


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).


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:

  • Call pipe. This gives you two file descriptors which you can convert to Handles with fdToHandle'.
  • Call createProcess with stdout and stderr both set to the pipe's write end.
  • Read child output from the pipe's read 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.

0

精彩评论

暂无评论...
验证码 换一张
取 消