我想从Haskell运行外部程序并检索其输出和错误流的内容。在其中一个库中,我找到了这段代码:
runProcess :: FilePath -> [String] -> IO (ExitCode, String, String)
runProcess prog args = do
(_,o,e,p) <- runInteractiveProcess prog args Nothing Nothing
hSetBuffering o NoBuffering
hSetBuffering e NoBuffering
sout <- hGetContents o
serr <- hGetContents e
ecode <- length sout `seq` waitForProcess p
return (ecode, sout, serr)
这是正确的做法吗?这里有一些我不明白的事情:为什么流设置为NoBuffering
?为什么length sout 'seq'
?这感觉就像某种黑客。此外,我想将输出和错误流合并为一个,以获得与在命令行上执行2>&1
时相同的效果。如果可能的话,我想避免使用专用的I / O库,并依赖GHC提供的标准软件包。
答案 0 :(得分:4)
使用Shelly,一个用于Haskell中类似shell编程的模块:
http://hackage.haskell.org/package/shelly-1.4.1/docs/Shelly.html
答案 1 :(得分:2)
此示例程序使用process
,async
,pipes
和pipes-bytestring
程序包执行外部命令并编写stdout
和{{1}在命令运行时分离文件:
stderr
这是一种将import Control.Applicative
import Control.Monad
import Control.Concurrent
import Control.Concurrent.Async
import Control.Exception
import Pipes
import qualified Pipes.ByteString as P
import Pipes.Concurrent
import System.Process
import System.IO
writeToFile :: Handle -> FilePath -> IO ()
writeToFile handle path =
finally (withFile path WriteMode $ \hOut ->
runEffect $ P.fromHandle handle >-> P.toHandle hOut)
(hClose handle)
main :: IO ()
main = do
(_,mOut,mErr,procHandle) <- createProcess $
(proc "foo" ["--help"]) { std_out = CreatePipe
, std_err = CreatePipe
}
let (hOut,hErr) = maybe (error "bogus handles")
id
((,) <$> mOut <*> mErr)
a1 <- async $ writeToFile hOut "stdout.txt"
a2 <- async $ writeToFile hErr "stderr.txt"
waitBoth a1 a2
return ()
和stdout
交错写入同一文件的变体:
stderr
它使用writeToMailbox :: Handle -> Output ByteString -> IO ()
writeToMailbox handle oMailbox =
finally (runEffect $ P.fromHandle handle >-> toOutput oMailbox)
(hClose handle)
writeToFile :: Input ByteString -> FilePath -> IO ()
writeToFile iMailbox path =
withFile path WriteMode $ \hOut ->
runEffect $ fromInput iMailbox >-> P.toHandle hOut
main :: IO ()
main = do
(_,mOut,mErr,procHandle) <- createProcess $
(proc "foo" ["--help"]) { std_out = CreatePipe
, std_err = CreatePipe
}
let (hOut,hErr) = maybe (error "bogus handles")
id
((,) <$> mOut <*> mErr)
(mailBoxOut,mailBoxIn,seal) <- spawn' Unbounded
a1 <- async $ writeToMailbox hOut mailBoxOut
a2 <- async $ writeToMailbox hErr mailBoxOut
a3 <- async $ waitBoth a1 a2 >> atomically seal
writeToFile mailBoxIn "combined.txt"
wait a3
return ()
。排出每个句柄的线程写入同一个邮箱。邮箱由主线程读取,主线程在命令运行时写入输出文件。
答案 2 :(得分:0)
为此,我发现readProcessWithExitCode
非常简洁。
这是一个仅使用GHC标准库中函数的示例,该函数列出了按大小排序的主目录文件,并打印进程的退出代码以及标准输出和标准错误流的内容:
import System.Directory ( getHomeDirectory )
import System.Process ( readProcessWithExitCode )
import System.Exit ( ExitCode )
import Data.List.NonEmpty
callCmd :: NonEmpty String -> IO (ExitCode, String, String)
callCmd (cmd :| args) = readProcessWithExitCode cmd args stdIn
where stdIn = ""
main = do
home <- getHomeDirectory
(exitCode, stdOut, stdErr) <-
callCmd $ "ls" :| [home, "--almost-all", "-l", "-S"]
putStrLn
$ "Exit code: "
++ show exitCode
++ "\nOut: "
++ stdOut
++ "\nErr: "
++ stdErr