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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With