如何检索从Haskell执行的外部程序的输出?

时间:2014-01-30 18:27:45

标签: haskell io

我想从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提供的标准软件包。

3 个答案:

答案 0 :(得分:4)

使用Shelly,一个用于Haskell中类似shell编程的模块:

http://hackage.haskell.org/package/shelly-1.4.1/docs/Shelly.html

答案 1 :(得分:2)

此示例程序使用processasyncpipespipes-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