Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to retrieve output of external program executed from Haskell?

Tags:

io

haskell

I want to run an external program from Haskell and retrieve the contents of its output and error streams. In one of the libraries I found this code:

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)

Is this the right way to do it?

There are some things I don't understand here: why streams are set to NoBuffering? Why length sout `seq`? This feels like some kind of hack.

Also, I would like to merge output and error streams into one to get the same effect as if I did 2>&1 on the command line. If possible, I want to avoid using dedicated I/O libraries and rely on standard packages provided with GHC.

like image 702
Jan Stolarek Avatar asked Jan 30 '14 18:01

Jan Stolarek


1 Answers

I find readProcessWithExitCode for this purpose very succinct.

Here's an example using only functions from GHC's standard libraries. The program lists the files of your home directory sorted by size, printing the process' exit code as well as the contents of the standard out and standard error streams:

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
like image 93
Matthias Braun Avatar answered Oct 16 '22 01:10

Matthias Braun