【问题标题】:Diagnostic.Process merge StandardOutput and StandardErrorOutputDiagnostic.Process 合并 StandardOutput 和 StandardErrorOutput
【发布时间】:2015-04-24 21:17:57
【问题描述】:

我正在使用 Process 类从 .NET 程序运行 cmd.exe,并传入各种命令来执行程序,即 ipconfig、netstat 等。问题是有时这些程序会输出错误文本,该文本会发送到 StandardError,但是无法知道在标准输出期间实际打印错误的确切位置。我得到的只是单独的错误消息。是否可以将 StandardOutput 和 StandardError 组合成一个同步流,以便我按顺序获取所有数据?如果不是,那么我该如何组织来自两个不同流的输出,使其按正确的顺序排列?

【问题讨论】:

  • 要合并您需要的流:按消息拆分数据,获取传入号码。然后您就可以将消息合并到一个集合中
  • 我不确定我是否理解?它是一个流,因此没有任何“消息”或“数字”与任何数据相关联。
  • 流是缓冲的,因此您需要解析传入的数据部分。如果您无法解析输出,则无法正确合并。
  • 这很容易,你可以让cmd.exe将两者合并。将神秘的 " 2>&1" 附加到其命令行参数中。现在所有输出都进入标准输出,您再也分不清正常输出和错误输出之间的区别了。
  • @HansPassant 你建议的是我正在寻找的东西,但是,它没有用?我在代码中使用了 'p.StartInfo.Arguments = "2>&1"' 并停止从 StandardError 流中读取。运行代码后,我根本没有得到任何错误输出。

标签: c# .net


【解决方案1】:

嗯。我想你不能真的期望 STDOUT 和 STDERR 以它们输出的确切顺序出现。这是因为在系统层面,STDOUT 和 STDERR 分别有自己的缓冲区。你可以想象这样的情况-

(1) 当“命令”先写入 STDOUT 时,缓冲区可能不会立即刷新。

(2) 然后“命令”写入 STDERR,但是这次 STDERR 的缓冲区可能会被刷新。

在这种情况下,即使 (1) 先出现,但我们最终得到的是 (2) 先出现。

希望下面的 sn-p 能有所帮助-

(改进的代码)

    class OutputContext
    {
        public const int BufferSize = 1024;

        public bool IsEof { get; set; }
        public bool IsWaiting { get; set; }
        public byte[] Buffer { get; set; }
        public StreamReader Reader { get; set; }
        public object Tag { get; set; }

        public OutputContext(StreamReader r, object tag)
        {
            IsEof = false;
            IsWaiting = false;
            Buffer = new byte[BufferSize];
            Reader = r;
            Tag = tag;
        }
    }

    Process proc;

    void Callback(IAsyncResult ar)
    {
        lock (ar.AsyncState)
        {
            OutputContext ctx = ar.AsyncState as OutputContext;
            int c = ctx.Reader.BaseStream.EndRead(ar);
            ctx.IsWaiting = false;

            if (c == 0)
            {
                ctx.IsEof = true;
                return;
            }

            string content = Encoding.UTF8.GetString(ctx.Buffer, 0, c);
            Console.Write(content);
            Console.Out.Flush();

        }
    }

    void RedirectOutput(OutputContext ctx)
    {
        lock (ctx)
        {
            if (ctx.IsEof)
            {
                return;
            }

            if (ctx.IsWaiting)
            {
                return;
            }

            ctx.IsWaiting = true;
            IAsyncResult ar = ctx.Reader.BaseStream.BeginRead(ctx.Buffer, 0, 
                OutputContext.BufferSize, Callback, ctx);
        }
    }

    void Run(string yourprog, string yourargs)
    {
        // If this is a GUI app, this shall not be run on the UI thread.
        // Spin a new thread to handle it and wait for the thread to complete.
        // And you can always accept the input as long as the UI thread is not
        // blocked, and redirect the input to the target proc's stdin asycly.

        proc = new Process();
        proc.StartInfo.CreateNoWindow = true;
        proc.StartInfo.FileName = yourprog;
        proc.StartInfo.Arguments = yourargs;
        proc.StartInfo.UseShellExecute = false;

        proc.StartInfo.RedirectStandardOutput = true;
        proc.StartInfo.RedirectStandardError = true;
        proc.StartInfo.RedirectStandardInput = true;

        proc.Start();

        OutputContext stdoutCtx = new OutputContext(proc.StandardOutput, "STDOUT");
        OutputContext stderrCtx = new OutputContext(proc.StandardError, "STDERR");

        while (!stdoutCtx.IsEof && !stderrCtx.IsEof)
        {
            RedirectOutput(stdoutCtx);
            RedirectOutput(stderrCtx);
        }

        proc.WaitForExit();
    }
}

【讨论】:

  • 使用 BeginOutputReadLine 的问题在于它不会输出任何期望输入的提示...因为其中没有回车\换行。只有在您提供一些输入并按 Enter 后,才会出现提示。这就是为什么我不得不放弃这种方法。
  • @tunafish24 是否需要与控制台进行任何用户交互?如果是这种情况,那就有点棘手了……因为 stdout 和 stderr 确实有 2 个缓冲区,所以您不知道 stdout 缓冲区中的一个字符是否比 stderr 缓冲区中的另一个早,反之亦然。 AFAIK,缓冲区在看到换行时会自动刷新。否则,需要强制刷新。在您的情况下,当有提示时,没有换行并且“命令”程序尚未完成,因此提示不会被刷新到控制台。让我考虑一下,稍后再回复您。
  • @tunafish24 我编辑了我的答案的代码。它解决了你的问题吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-19
  • 2018-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多