读取使用 createProcess 获得的管道的几行,然后将其关闭

Posted

技术标签:

【中文标题】读取使用 createProcess 获得的管道的几行,然后将其关闭【英文标题】:Reading a couple of lines of a pipe obtained with createProcess and then closing it 【发布时间】:2017-11-01 13:25:22 【问题描述】:

我有一个产生如下进程的函数:

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

所以"someproc"被创建,然后父级从它读取一个line获取一些信息,然后它忘记了句柄为outh的管道。

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

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

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

作为附加背景,这个问题与this one有关。

【问题讨论】:

【参考方案1】:

现在的问题是,如果管道没有被读取,“someproc”一满就会阻塞。 [...] Haskell 有什么方法可以自动丢弃 输入到管道(甚至重定向到文件)?

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

该库不直接使用句柄,但接受 pipe 消耗函数和 foldl 通过 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 要大得多。

【讨论】:

【参考方案2】:

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

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

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

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

应该在恒定空间中运行。

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

【讨论】:

doStuff 在生成的进程产生某些内容之前尝试从outh 读取,这是否也会导致“文件结束”错误? @DamianNadales doStuff 绝不能从outh 读取——上面代码的重点是从outh 读取所有内容并将其丢弃。 这就是问题所在,我只需要读取一行,然后丢弃其余的 :) 否则我可以将其重定向到文件。 @DamianNadales 那么问题是什么?先用getLine读一行,然后用上面的代码丢弃其余的。 问题是,如果我在进程写入内容之前尝试读取,父进程不会阻塞但会收到文件结束错误。

以上是关于读取使用 createProcess 获得的管道的几行,然后将其关闭的主要内容,如果未能解决你的问题,请参考以下文章

无法从通过管道 C++ 重定向的程序中获得正确的输出(Windows)

与 CreateNamedPipe 重叠的 CreateProcess 标准输出

在输出管道上拒绝重定向访问的 CreateProcess

CreateProcess cmd.exe 读/写管道死锁

如果管道已满,写入管道的进程是不是会阻塞?

CreateProcess 无法运行 sfc.exe ?为啥