在haskell捕获/劫持stdout

时间:2012-02-25 20:14:46

标签: haskell stdout handle

如何定义'catchOutput'以便仅运行主输出'bar'?

也就是说,如何分别访问输出流(stdout)和io动作的实际输出?

catchOutput :: IO a -> IO (a,String)
catchOutput = undefined

doSomethingWithOutput :: IO a -> IO ()
doSomethingWithOutput io = do
   (_ioOutp, stdOutp) <- catchOutput io
   if stdOutp == "foo"
      then putStrLn "bar"
      else putStrLn "fail!"

main = doSomethingWithOutput (putStr "foo")

到目前为止我发现的最好的假设“解决方案”包括将stdout inspired by this转移到文件流,然后从该文件中读取(除了超级丑陋之外我还没有能够从文件写入后直接读取。是否可以创建一个不必存储在文件中的“自定义缓冲流”?)。虽然感觉“有点像'侧面轨道。”

另一个角度似乎是使用'hGetContents stdout',如果它应该按我认为的那样做。但我没有得到stdout的阅读许可。虽然谷歌搜索似乎表明它已经used

4 个答案:

答案 0 :(得分:4)

为什么不使用作家monad呢?例如,

import Control.Monad.Writer

doSomethingWithOutput :: WriterT String IO a -> IO ()
doSomethingWithOutput io = do
   (_, res) <- runWriterT io
   if res == "foo"
      then putStrLn "bar"
      else putStrLn "fail!"

main = doSomethingWithOutput (tell "foo")

或者,您可以修改内部操作,以便Handle代替stdout。然后,您可以使用类似knob的内容来创建内存文件句柄,您可以将其传递给内部操作,然后检查其内容。

答案 1 :(得分:4)

我使用以下函数进行打印到stdout的函数的单元测试。

import GHC.IO.Handle
import System.IO
import System.Directory

catchOutput :: IO () -> IO String
catchOutput f = do
  tmpd <- getTemporaryDirectory
  (tmpf, tmph) <- openTempFile tmpd "haskell_stdout"
  stdout_dup <- hDuplicate stdout
  hDuplicateTo tmph stdout
  hClose tmph
  f
  hDuplicateTo stdout_dup stdout
  str <- readFile tmpf
  removeFile tmpf
  return str

我不确定内存中的文件方法,但是对于带有临时文件的少量输出它可以正常工作。

答案 2 :(得分:4)

Hackage上有一些软件包可以做到这一点:io-capture和静默。默默地似乎维护并在Windows上工作(io-capture仅适用于Unix)。静默地使用捕获:

import System.IO.Silently

main = do
   (output, _) <- capture $ putStr "hello"
   putStrLn $ output ++ " world"

请注意,它可以通过将输出重定向到临时文件然后读取它来工作......但只要它有效!

答案 3 :(得分:1)

正如@hammar所指出的,您可以使用旋钮创建内存文件,但您也可以使用hDuplicatehDuplicateTostdout更改为内存文件,又回来了。类似于以下完全未经测试的代码:

catchOutput io = do
  knob <- newKnob (pack [])
  let before = do
        h <- newFileHandle knob "<stdout>" WriteMode
        stdout' <- hDuplicate stdout
        hDuplicateTo h stdout
        hClose h
        return stdout'
      after stdout' = do
        hDuplicateTo stdout' stdout
        hClose stdout'
  a <- bracket_ before after io
  bytes <- Data.Knob.getContents knob
  return (a, unpack bytes)