我想从haskell程序中调用一个进程并捕获stdout以及stderr。
我的所作所为:
(_, stdout, stderr) <- readProcessWithExitCode "command" [] ""
问题:这样,stdout和stderr被单独捕获,但我希望消息出现在正确的位置(否则我只是stdout ++ stderr
将错误消息与其stdout对应物分开)。
我知道如果我将输出传输到文件中,即
,我就可以实现这一点tmp <- openFile "temp.file" ...
createProcess (proc "command" []) { stdout = UseHandle tmp,
stderr = UseHandle tmp }
所以我目前的解决方法是将输出管道传输到临时文件并重新读取。但是我正在寻找更直接的方法。
如果我在unix上肯定我只是调用一个shell命令ála
command 2>&1
就是这样。但是,我希望尽可能便携。
我需要这个:我已经构建了一个小的haskell cgi脚本(只是为了玩它),它调用某个程序并打印输出。我想html-escape输出,因此我不能简单地将它传递给stdout。
我在想:也许可以创建一个内存中的句柄,比如Java中的PipedInputStream / PipedOutputStream,或ArrayInputStream / ArrayOutputStream,它允许在内存中处理IO流。我在hoogle上四处查找功能:: Handle
,但没有找到任何内容。
也许还有另一个Haskell模块允许我合并两个流?
答案 0 :(得分: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。
答案 1 :(得分:4)
对于posix系统,你可以在createPipe
中使用fdToHandle
和System.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
。