如何定义'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。
答案 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所指出的,您可以使用旋钮创建内存文件,但您也可以使用hDuplicate
和hDuplicateTo
将stdout
更改为内存文件,又回来了。类似于以下完全未经测试的代码:
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)