读取通过 createProcess 获得的管道的几行,然后关闭它

Reading a couple of lines of a pipe obtained with createProcess and then closing it

我有一个生成进程的函数,如下所示:

(_, Just outh, _, ph) <- createProcess $
    (proc "someproc" []) { std_out = CreatePipe }
line <- hGetLine outh
doSomeComputationOn line
-- ... 'outh' is not read anymore from here onwards

所以创建了"someproc",然后parent从里面读取了一个line来获取一些信息,然后忘记了句柄为outh的管道。

现在的问题是,如果管道不读,"someproc"一满就会阻塞。所以这要求父进程读取 outh 即使它没有对它做任何事情。所以我的问题是:

  1. 这是获取子进程输出的第一行然后忘记其他输出的好方法吗?
  2. 在 Haskell 中有什么方法可以自动丢弃管道的输入(甚至将其重定向到文件)?

到目前为止,我能看到解决这个问题的唯一方法是生成一个新线程,不断尝试从 outh 读取(并丢弃输出),这表明我做错了什么...

作为附加背景,此问题与

有关

要使用的替代方法取决于外部命令的行为。

如果你只是想打断它,你可以hClose outh。这将关闭管道,并且通过外部命令进一步写入管道将失败并出现 "broken pipe" 错误。大多数进程在收到此消息后终止。

如果您想要读取并丢弃输出,您也可以这样做。也许最简单的方法是

do c <- hGetContents outh
   evaluate (length c)  -- force this to fetch all data
   doStuff              -- now we are sure that the remote end closed its output

应该 运行 常量 space。

如果您不想等待进程结束再执行doStuff,请将所有内容包装在forkIO.

Now the problem is that if the pipe is not read, "someproc" will block as soon as it is full. [...] Is there any way in Haskell in which I can automatically discard the input to the pipe (or even redirect it to a file)?

process 有一个名为 process-streaming 的帮助程序库(由本答案的作者编写)试图做到这一点:即使用户通过不会耗尽标准流的流消耗函数,它会在引擎盖下自动排出流以避免潜在的死锁。

该库不直接使用句柄,但接受 pipe-consuming functions and foldl folds through an adapter type

读第一行的例子:

import           Data.Text.Lazy
import qualified Pipes.Prelude
import           System.Process.Streaming      (CreateProcess,shell,
                                                piped,execute,foldOut,transduce1,withCont)
import           System.Process.Streaming.Text (utf8x,foldedLines)

program :: CreateProcess
program = piped $ shell "{ echo aaa ; echo bbbb ; }"

firstLine :: IO (Maybe Text)
firstLine = execute program streams
    where
    streams = foldOut
            . transduce1 utf8x
            . transduce1 foldedLines
            $ withCont Pipes.Prelude.head

尽管库的依赖足迹比 process 大得多。