2012-02-25 6 views
10

mainを実行すると 'bar'だけが出力されるように 'catchOutput'をどのように定義できますか?haskellでstdoutをキャッチ/ハイジャックする

つまり、出力ストリーム(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") 

最良の仮説的な「解決策」私が見つけた、これまでに含まれるファイルストリームに、標準出力、inspired by thisを流用して、私は読むことができなかった超醜いであることに加え、そのファイル(からの読み取りファイルから直接書き込んだ後で、ファイルに格納する必要のない「カスタムバッファストリーム」を作成することは可能ですか?)。それはサイドトラックのような '少しビット'を感じるが。

「hGetContents stdout」が別の角度で使用されていると思われます。しかし、私はstdoutから読むことが許可されていません。グーグルでは、それはusedであることを示すようだ。

+0

あなたの使用状況に応じて、移植性が問題であるかどうかによって、この実験的ライブラリを使用できます。 https://hackage.haskell.org/package/system-posix-redirect-1.1.0.1/docs/System-Posix -Redirect.html –

答えて

4

代わりにライターモナドを使用するのはなぜですか?例えば、

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") 

はまた、あなたの代わりにstdoutへの書き込みにHandleを取るためにあなたの内のアクションを変更することができます。 knobのようなものを使用して、メモリ内のファイルハンドルを作成し、内側のアクションに渡して、その内容を後で確認することができます。

+0

実際のコードでは、ioの引数については実際にはあまりできません。それは外向きの振る舞いを再設計する必要があります。 "クライアントアプリケーション"は、(引数として)ioアクションを送信/渡すことになっています。 したがって、ある時点では、「catchOutput」のようなものを使用する必要があります。だから作家のモナドは本当に私の問題には当てはまりません。 一方、ノブは潜在的に役に立つと思われます。 :) – worldsayshi

+1

@worldsayshiあなたはハンマーの提案を誤解しているようです。彼は 'Writer'モナドではなく' WriterT a IO'モナドを使用していることに注意してください。あなたは 'liftIOio'を使って' IO'アクションを呼び出すことができます。 –

+0

私は自分のコードを修正しましたが、残念です。おそらくそれによっていくつかの混乱。私が最初に探しているのは、テキストがstdoutで終わることです。それは私の例では_now_resです。私はliftIOが私にそれへのアクセスを与える方法を見ない。次に、実際にioアクションを実行することも出力を欲しがります。これは現在、 'catchOutput'から無視される出力です。 引数として「IO()」を取得すると、「m()」が表示されます。 – worldsayshi

1

@hammarが指摘したように、あなたは、メモリ内のファイルを作成するために、ノブを使用することができますが、あなたはまた、再びメモリファイルにstdoutを変更するhDuplicatehDuplicateToを使用して、することができます。次の完全にテストされていないコードのようなもの:

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) 
+3

ええと、実際に 'Data.Knob'をインストールしたので、私はこのメソッドを動作させることができません。 'hDuplicateTo'への呼び出しは、ノブハンドルを' stdout'に複製しようとすると失敗します。 'Wots::hDuplicateTo:不正な操作(ハンドルは互換性がありません) '。 – pat

3

Iが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 

Iは、メモリ内のファイルのアプローチについてはよく分からないですが、それは一時的なファイルに出力少量のために大丈夫動作します。

4

これを行うことを約束しているHackageにはいくつかのパッケージがあります:io-captureとサイレント。静かに維持されているようで、Windowsでも動作します(io-captureはUnix上でのみ動作します)。限り、それが動作するよう

import System.IO.Silently 

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

注それは一時的な出力をファイルにリダイレクトすることで動作し、それを読むことを...しかし:静かで、あなたは、キャプチャを使用します!

関連する問題