忽略进程的标准错误

Ignoring stderr of process

这应该是一个简单的问题,但到目前为止我还没有找到任何直接的答案:how does one ignore the stderr (or stdout) of使用 process 的 Haskell 进程?例如,假设我有以下内容:

let proc = (shell "dir /z") {
      std_in  = UseHandle stdin
    , std_out = CreatePipe
    }
(_, out, _, rProc) <- createProcess Proc
exitCode <- waitForProcess rProc

(旁注:在 Windows 中,我知道 dir 没有 /z 开关。这就是我选择它的原因——这样我可以获得一些有趣的输出在 stderr.)

这样做只会导致 stderr 被打印到控制台。现在假设我想忽略 stderr。我该怎么做?

我找到的唯一线索在 process 文档的 this part 中:

NoStream Close the stream's file descriptor without passing a Handle. On POSIX systems this may lead to strange behavior in the child process because attempting to read or write after the file has been closed throws an error. This should only be used with child processes that don't use the file descriptor at all. If you wish to ignore the child process's output you should either create a pipe and drain it manually or pass a Handle that writes to /dev/null.

这有点帮助,但仍有一些问题没有得到解答。在非 POSIX 系统上,可以使用 NoStream 吗?它指的是创建一个管道然后将其排出,但我找不到任何关于如何做到这一点的信息? /dev/null 在 Windows 上是 NUL,除非你使用 MSYS 或 Cygwin,当它又是 /dev/null 时(我认为)——所以我想避免这种情况。

所以重申我的问题:推荐的 OS 忽略进程的 stderr 不可知的方法是什么?

我想 nullStream in typed-process package 就是您要找的。

这是一种正确的方法:

import Control.Concurrent
import Control.Exception
import System.IO
import System.Process

forceGetContents :: String -> IO ()
forceGetContents s = evaluate (length s) >> return ()

main = do
    outMVar <- newEmptyMVar
    let proc = (shell "dir /z") {
          std_in  = UseHandle stdin
        , std_out = CreatePipe
        , std_err = CreatePipe
        }
    (_, Just out, Just err, rProc) <- createProcess proc
    forkIO (hGetContents err >>= forceGetContents)
    forkIO $ do
        s <- hGetContents out
        forceGetContents s
        putMVar outMVar s
    exitCode <- waitForProcess rProc
    outContents <- takeMVar outMVar
    putStr outContents -- or whatever

对已删除答案的评论中值得注意的一些事项:

  1. 您应该分叉一个线程来排出错误管道。否则,如果有很多错误,您启动的进程可能会在打印所有错误之前被杀死,从而导致调试会话混乱。
  2. 如果你要waitForProcess,你应该fork一个线程来排空输出管道。否则它可能会在打印它想要的所有内容之前被杀死,从而给出不完整的输出。
  3. 这会将整个过程的输出(尽管不是错误流的全部内容)存储在内存中。这可能非常昂贵。
  4. forceGetContents 是强制对 hGetContents 返回的 String 进行全面评估的好方法,但还有其他 String-生产者可能需要更多涉及强制功能。另请参阅 deepseq 包中的 rnf

如果有一个您知道流程将遵循的协议,您可以同时解决 (2) 和 (3),您将知道何时完成输出。然后你可以流式输出(减少可以提前丢弃的位的内存压力),并且可以延迟 waitForProcess 直到你知道它完成输出(避免需要分叉一个线程来耗尽输出 - 虽然仍然需要一个分支线程来解决错误!)。