【问题标题】:Ignoring stderr of process忽略进程的stderr
【发布时间】:2019-11-20 15:11:28
【问题描述】:

这应该是一个简单的问题,但到目前为止我还没有找到任何直接的答案:如何使用 process 忽略 Haskell 进程的 stderr(或 stdout)图书馆?例如,假设我有以下内容:

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 关闭流的文件描述符而不传递句柄。在 POSIX 系统上,这可能会导致子进程出现奇怪的行为,因为在文件关闭后尝试读取或写入会引发错误。这应该只用于根本不使用文件描述符的子进程。如果您希望忽略子进程的输出,您应该创建一个管道并手动将其排空,或者传递一个写入/dev/nullHandle

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

所以重申我的问题:什么是推荐的、与操作系统无关的方法来忽略进程的stderr

【问题讨论】:

    标签: haskell process stderr


    【解决方案1】:

    我猜nullStream in typed-process package 就是你要找的。​​p>

    【讨论】:

    • 这绝对是我平时会用的,可惜由于种种原因我不能用typed-process。不过还是谢谢你的回答!
    【解决方案2】:

    这是一种正确的方法:

    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
    

    cmets 对已删除答案的一些注意事项:

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

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

    【讨论】:

    • 为什么进程在输出耗尽之前被杀死会出现问题?操作系统不应该将其全部缓冲在管道中吗?
    • 分叉一个线程来排除错误绝对是有意义的(或者,可能更好),使用 Yuji Yamamoto 的建议),但这样做似乎很不幸。
    • @dfeuer 管道缓冲区一般为 4KB 左右。所以如果有超过 4KB 的输出,不,操作系统不会全部缓冲。
    • 我已经阅读了这个答案,但我仍然不明白第 (1) 点。如果您只是忽略错误,那么如果进程在打印所有错误之前被终止,那又有什么关系呢?还有,对于(2),如果使用waitForProcess,为什么在程序完成之前输出管道会被杀死?
    • @bradrn 这很重要,因为错误和实际输出可以交错。因此,如果它在完成打印错误之前被杀死,因为它的错误管道已满,也可能会有你永远看不到的输出,因为程序在打印它之前就被杀死了!
    猜你喜欢
    • 1970-01-01
    • 2017-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 2013-06-24
    • 1970-01-01
    相关资源
    最近更新 更多