首页 文章

如何将输出从IO操作传输到haskell中的进程

提问于
浏览
4

我想创建一个进程并定期将我的haskell程序中的一些文本写入进程的stdin(来自IO操作) .

以下在GHCi中正常工作,但在构建和运行时无法正常工作 . 在GHCi中,一切都运行良好,IO动作的 Value 定期提供 . 然而,当构建并运行时,它似乎在写入进程的标准输入时暂停任意长时间 .

我已经使用 CreateProcess (来自 System.Process )来创建句柄并尝试 hPutStrLn (bufferent设置为 NoBuffering - LineBuffering 也没有工作) .

所以我正在尝试 process-streaming 包和 pipes 但似乎无法完成任何工作 .

真正的问题是:我如何从haskell创建一个进程并定期写入它?

展示此行为的最小示例:

import System.Process
import Data.IORef
import qualified Data.Text as T  -- from the text package
import qualified Data.Text.IO as TIO
import Control.Concurrent.Timer  -- from the timers package
import Control.Concurrent.Suspend -- from the suspend package

main = do
    (Just hin, _,_,_) <- createProcess_ "bgProcess" $
        (System.Process.proc "grep"  ["10"]) { std_in = CreatePipe }

    ref <- newIORef 0 :: IO (IORef Int)
    flip repeatedTimer (msDelay 1000) $ do
        x <- atomicModifyIORef' ref $ \x -> (x + 1, x)
        hSetBuffering hin NoBuffering
        TIO.hPutStrLn hin $ T.pack $ show x

任何帮助将不胜感激 .

2 回答

  • 3

    这是一个管道 Producer ,它发出一系列带有第二个延迟的数字:

    {-# language NumDecimals #-}
    import Control.Concurrent
    import Pipes
    import qualified Data.ByteString.Char8 as Bytes
    
    periodic :: Producer Bytes.ByteString IO ()
    periodic = go 0
        where
            go n = do
                d <- liftIO (pure (Bytes.pack (show n ++ "\n"))) -- put your IO action here
                Pipes.yield d
                liftIO (threadDelay 1e6)
                go (succ n)
    

    并且,使用process-streaming,我们可以将 生产环境 者提供给外部流程,如下所示:

    import System.Process.Streaming
    
    main :: IO ()
    main = do
        executeInteractive (shell "grep 10"){ std_in = CreatePipe } (feedProducer periodic)
    

    我使用了executeInteractive,它将 std_in 自动设置为 NoBuffering .

    此外,如果您管道 std_out 并希望立即处理每个匹配项,请确保将 --line-buffered 选项传递给grep(或使用stdbuf命令)以确保在输出中立即可以使用匹配项 .

  • 0

    那么使用 threadDelay ,例如:

    import Control.Monad (forever)
    import Control.Concurrent (threadDelay)
    ...
    
    forever $ do
        x <- atomicModifyIORef' ref $ \x -> (x + 1, x)
        hSetBuffering hin NoBuffering
        TIO.hPutStrLn hin $ T.pack $ show x
        threadDelay 1000000  -- 1 sec
    

    如果您需要同时执行其他工作,请在另一个线程中生成此项 .

    您可以通过以下方式删除他对IORef的需求:

    loop h x = do 
        hSetBuffering h NoBuffering
        TIO.hPutStrLn h $ T.pack $ show x
        threadDelay 1000000
        loop h (x+1)
    

    当然,你只需要做一次 hSetBuffering ,例如在你进入循环之前做这件事 .

相关问题