2012-12-24 71 views
9

我想从一个haskell程序中调用一个进程并捕获标准输出以及标准错误。Haskell:System.Process合并标准输出和stderr

我做什么:

(_, stdout, stderr) <- readProcessWithExitCode "command" [] "" 

的问题:这样一来,输出和错误是分开拍摄的,但是我需要的邮件出现在正确的地方(否则我就简单stdout ++ stderr用于分隔错误信息他们的stdout同行)。

我知道我能做到这一点,如果我管输出到一个文件,即

tmp <- openFile "temp.file" ... 
createProcess (proc "command" []) { stdout = UseHandle tmp, 
            stderr = UseHandle tmp } 

所以我目前的解决方法是将管道输出到一个临时文件和读回。不过,我正在寻找更直接的方法。

如果我是在UNIX上肯定我会简单地调用一个shell命令一拉

command 2>&1 

,就是这样。不过,我希望尽可能便携。我建立了一个很小的haskell cgi脚本(只是为了玩它),它调用某个程序并打印输出。我想html转义输出,因此我不能简单地将其输出到标准输出。

我在想:也许可以创建一个内存中的句柄,比如Java中的PipedInputStream/PipedOutputStream,或者允许在内存中处理IO流的ArrayInputStream/ArrayOutputStream。我在hoogle上寻找了一个功能:: Handle,但没有找到任何东西。

也许有另一个Haskell模块在那里允许我合并两个流?

+0

也许['createPipe'](http://hackage.haskell.org/packages/archive/unix/latest/doc/html/System-Posix-IO-ByteString.html#v:createPipe)和['fdToHandle '](http://hackage.haskell.org/packages/archive/unix/latest/doc/html/System-Posix-IO-ByteString.html#v:fdToHandle)将有所帮助。 –

回答

6

您可以使用pipes来同时合并两个输入流。第一招是从两个流同时读取,你可以使用stm包做:

import Control.Applicative 
import Control.Proxy 
import Control.Concurrent 
import Control.Concurrent.STM 
import System.Process 

toTMVarC :: (Proxy p) => TMVar a ->() -> Consumer p a IO r 
toTMVarC tmvar() = runIdentityP $ forever $ do 
    a <- request() 
    lift $ atomically $ putTMVar tmvar a 

fromTMVarS :: (Proxy p) => TMVar a ->() -> Producer p a IO r 
fromTMVarS tmvar() = runIdentityP $ forever $ do 
    a <- lift $ atomically $ takeTMVar tmvar 
    respond a 

我将很快提供一个pipes-stm包装上面的原语,但使用上面的现在。

然后你只给每个Handle到一个单独的MVar并从两个同时读取:

main = do 
    (_, mStdout, mStderr, _) <- createProcess (proc "ls" []) 
    case (,) <$> mStdout <*> mStderr of 
     Nothing    -> return() 
     Just (stdout, stderr) -> do 
      out <- newEmptyTMVarIO 
      err <- newEmptyTMVarIO 
      forkIO $ runProxy $ hGetLineS stdout >-> toTMVarC out 
      forkIO $ runProxy $ hGetLineS stderr >-> toTMVarC err 
      let combine() = runIdentityP $ forever $ do 
        str <- lift $ atomically $ 
         takeTMVar out `orElse` takeTMVar err 
        respond str 
      runProxy $ combine >-> putStrLnD 

与然而你要处理的输入只要改变了putStrLnD

要了解有关pipes package的更多信息,请阅读Control.Proxy.Tutorial

3

对于POSIX系统,你可以使用createPipefdToHandleSystem.Posix.IO打造一双新的句柄(我不知道在哪里可以关闭这些句柄和FDS虽然..):

readProcessWithMergedOutput :: String -> IO (ExitCode, String) 
    readProcessWithMergedOutput command = do 
    (p_r, p_w) <- createPipe 
    h_r <- fdToHandle p_r 
    h_w <- fdToHandle p_w 
    (_, _, _, h_proc) <- createProcess (proc command []) 
             { std_out = UseHandle h_w 
             , std_err = UseHandle h_w 
             } 
    ret_code <- waitForProcess h_proc 
    content <- hGetContents h_r 
    return (ret_code, content) 

对于Windows, this post实施了跨平台createPipe

相关问题