9

haskellプログラム内からプロセスを呼び出し、stdoutとstderrをキャプチャしたいと思います。

私がやること:

(_, stdout, stderr) <- readProcessWithExitCode "command" [] ""

問題:このように、stdoutとstderrは別々にキャプチャされますが、メッセージを適切な場所に表示する必要があります(そうでない場合は、stdout ++ stderrエラーメッセージを対応するstdoutから単純に分離します)。

出力をファイルにパイプする場合、つまり、これを達成できることを私は知っています。

tmp <- openFile "temp.file" ...
createProcess (proc "command" []) { stdout = UseHandle tmp,
                                    stderr = UseHandle tmp }

したがって、現在の回避策は、出力を一時ファイルにパイプして読み戻すことです。ただし、より直接的なアプローチを探しています。

確かにUNIXを使用している場合は、シェルコマンドを呼び出すだけです。

command 2>&1

以上です。ただし、これはできるだけポータブルにしたいと思います。

これが必要な理由:特定のプログラムを呼び出して出力を出力する小さなhaskell cgiスクリプト(それで遊ぶためだけに)を作成しました。出力をhtmlエスケープしたいので、単純にstdoutにパイプすることはできません。

私が考えていたのは、JavaのPipedInputStream / PipedOutputStreamや、メモリ内でIOストリームを処理できるArrayInputStream/ArrayOutputStreamのようなメモリ内ハンドルを作成できる可能性があるということです。hoogleの関数を探しました:: Handleが、何も見つかりませんでした。

たぶん、2つのストリームをマージできる別のHaskellモジュールがありますか?

4

2 に答える 2

6

を使用pipesして、2つの入力ストリームを同時にマージできます。stm最初のトリックは、2つのストリームから同時に読み取ることです。これは、パッケージを使用して実行できます。

import Control.Applicative
import Control.Proxy
import Control.Concurrent
import Control.Concurrent.STM
import System.Process

toTMVarC :: (Proxy p) => TMVar a -> () -> Consumer p a IO r
toTMVarC tmvar () = runIdentityP $ forever $ do
    a <- request ()
    lift $ atomically $ putTMVar tmvar a

fromTMVarS :: (Proxy p) => TMVar a -> () -> Producer p a IO r
fromTMVarS tmvar () = runIdentityP $ forever $ do
    a <- lift $ atomically $ takeTMVar tmvar
    respond a

すぐに上記のプリミティブをpipes-stmパッケージで提供しますが、今は上記を使用します。

Handle次に、それぞれを別々にフィードしMVar、両方から同時に読み取ります。

main = do
    (_, mStdout, mStderr, _) <- createProcess (proc "ls" [])
    case (,) <$> mStdout <*> mStderr of
        Nothing               -> return ()
        Just (stdout, stderr) -> do
            out <- newEmptyTMVarIO
            err <- newEmptyTMVarIO
            forkIO $ runProxy $ hGetLineS stdout >-> toTMVarC out
            forkIO $ runProxy $ hGetLineS stderr >-> toTMVarC err
            let combine () = runIdentityP $ forever $ do
                    str <- lift $ atomically $
                        takeTMVar out `orElse` takeTMVar err
                    respond str
            runProxy $ combine >-> putStrLnD

putStrLnD入力を処理したい方法で変更してください。

pipesパッケージの詳細については、Control.Proxy.Tutorialをお読みください。

于 2012-12-24T21:56:56.117 に答える
4

posixシステムの場合、新しいハンドルのペアを作成するために使用できますcreatePipefdToHandleただしSystem.Posix.IO、これらのハンドルとfdsをどこで閉じるかはわかりません。):

  readProcessWithMergedOutput :: String -> IO (ExitCode, String)
  readProcessWithMergedOutput command = do
    (p_r, p_w) <- createPipe
    h_r <- fdToHandle p_r
    h_w <- fdToHandle p_w
    (_, _, _, h_proc) <- createProcess (proc command [])
                                       { std_out = UseHandle h_w
                                       , std_err = UseHandle h_w
                                       }
    ret_code <- waitForProcess h_proc
    content <- hGetContents h_r
    return (ret_code, content)

Windowsの場合、この投稿はクロスプラットフォームを実装しましたcreatePipe

于 2012-12-25T03:18:20.467 に答える