【问题标题】:How to asynchronously read the standard output stream and standard error stream at once如何一次异步读取标准输出流和标准错误流
【发布时间】:2012-09-15 23:30:54
【问题描述】:

我想以控制台中的形式读取进程的输出(标准输出与一个流中的标准错误混合)。有什么办法吗?

我正在考虑使用

ProcessStartInfo.UseShellExecute = true;  

但是我无法异步读取输出。如果我设置

process.ProcessStartInfo.UseShellExecute = false;  
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += new DataReceivedEventHandler(partialOutputHandler);

然后我可以读取标准输出(我可以对标准错误执行相同的操作)但我不知道如何模拟控制台的行为(stdout 和 stderr 的混合)。

这类似于Linux,具有将标准错误流重定向到标准输出流的特性;怎么样?

【问题讨论】:

    标签: c# .net process


    【解决方案1】:

    你的意思是这样的吗?

    SynchronizationContext _syncContext;
    MyForm()
    {
        _syncContext = SynchronizationContext.Current;
    }
    
    void StartProcess()
    {
        using (var process = new Process
            {
                StartInfo = new ProcessStartInfo
                {
                    FileName = "myProcess.exe",
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                }
            })
        {
            process.OutputDataReceived += (sender, args) => Display(args.Data);
            process.ErrorDataReceived += (sender, args) => Display(args.Data);
    
            process.Start();
            process.BeginOutputReadLine();
            process.BeginErrorReadLine();
    
            process.WaitForExit(); //you need this in order to flush the output buffer
        }   
    }
    
    void Display(string output)
    {
        _syncContext.Post(_ => myTextBox.AppendText(output), null);
    }
    

    【讨论】:

    • 是的,这是我最终得到的代码,因为我的要求是不可能的。谢谢!
    • 很好的例子——让我摆脱了 sqlcmd 的束缚。请务必调用 Process.Dispose()。
    【解决方案2】:

    MSDN article 声明:

    重定向的 StandardError 流可以被同步读取或 异步。 Read、ReadLine 和 ReadToEnd 等方法执行 对进程的错误输出流进行同步读操作。 这些同步读取操作直到关联的 进程写入其 StandardError 流,或关闭该流。

    相比之下,BeginErrorReadLine 在 标准错误流。此方法启用指定事件 流输出的处理程序并立即返回给调用者, 它可以在流输出定向到时执行其他工作 事件处理程序。

    同步读取操作会在调用者之间引入依赖关系 从 StandardError 流读取和子进程写入 那个流。这些依赖关系可能导致死锁情况。 当调用者从子进程的重定向流中读取数据时, 它取决于孩子。调用者等待读取操作 直到孩子写入流或关闭流。当。。。的时候 子进程写入足够的数据来填充其重定向的流,它是 依赖于父母。子进程等待下一次写入 操作,直到父级从完整流中读取或关闭 溪流。当调用者和孩子发生死锁条件 进程互相等待完成一个操作,两者都不能 继续。您可以通过评估之间的依赖关系来避免死锁 调用者和子进程。

    这同样适用于StandardOutput,因此您只需异步读取两个流。

    Merging 两个流合二为一会使检测什么输出是错误报告以及什么是“产品”信息变得复杂。

    【讨论】:

    • 我写道,我可以从两个流中读取。而且我知道合并并不总是好的,但在我的特殊情况下,这是我需要的。我只是不知道如何像控制台那样模拟stderr和stdout的交错。
    • 您是否尝试为StadnardErrorStandardOutput 设置相同的流?
    • 是的。但它的行为不像我想要的那样。
    【解决方案3】:

    我找到了答案:

    输出流被缓冲。没有办法得到真实的 插入到流中的项目的顺序。事实上它 没有什么意义,因为两个流也可以同时编写 时间。它们彼此独立。因此,你可以做到最好 要做的是在每个到达时获取它们的顺序输出。

    虽然几乎所有控制台应用程序都使用 输出和错误消息的标准输出。错误流 由某些应用程序使用,但消息通常与 输出流中产生的错误。

    来源: http://social.msdn.microsoft.com/Forums/uk/csharpgeneral/thread/192b6df7-9437-42cf-81c1-c125021735ba

    【讨论】:

    • 好吧,这不是您原来问题的答案。
    【解决方案4】:

    类似的示例,除了我为此目的使用 StringBuilder 将标准输出和错误收集到单独的字符串中。

    /// <summary>
    /// Executes command
    /// </summary>
    /// <param name="cmd">command to be executed</param>
    /// <param name="output">output which application produced</param>
    /// <param name="transferEnvVars">true - if retain PATH environment variable from executed command</param>
    /// <returns>true if process exited with code 0</returns>
    static bool ExecCmd(string cmd, out String output, bool transferEnvVars = false)
    {
        ProcessStartInfo processInfo;
        Process process;
    
        if (transferEnvVars)
            cmd =  cmd + " && echo --VARS-- && set";
    
        processInfo = new ProcessStartInfo("cmd.exe", "/c " + cmd);
        processInfo.CreateNoWindow = true;
        processInfo.UseShellExecute = false;
        processInfo.RedirectStandardError = true;
        processInfo.RedirectStandardOutput = true;
    
        process = Process.Start(processInfo);
    
        // Executing long lasting operation in batch file will hang the process, as it will wait standard output / error pipes to be processed.
        // We process these pipes here asynchronously.
        StringBuilder so = new StringBuilder();
        process.OutputDataReceived += (sender, args) => { so.AppendLine(args.Data); };
        StringBuilder se = new StringBuilder();
        process.ErrorDataReceived += (sender, args) => { se.AppendLine(args.Data); };
    
        process.BeginOutputReadLine();
        process.BeginErrorReadLine();
        process.WaitForExit();
    
        output = so.ToString();
        String error = se.ToString();
    
        if (transferEnvVars)
        {
            Regex r = new Regex("--VARS--(.*)", RegexOptions.Singleline);
            var m = r.Match(output);
            if (m.Success)
            {
                output = r.Replace(output, "");
    
                foreach ( Match m2 in new Regex("(.*?)=([^\r]*)", RegexOptions.Multiline).Matches(m.Groups[1].ToString()) )
                {
                    String key = m2.Groups[1].Value;
                    String value = m2.Groups[2].Value;
                    Environment.SetEnvironmentVariable(key, value);
                }
            }
        }
    
        if(error.Length != 0)
            output += error;
        int exitCode = process.ExitCode;
    
        if (exitCode != 0)
            Console.WriteLine("Error: " + output + "\r\n" + error);
    
        process.Close();
        return exitCode == 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-25
      • 1970-01-01
      • 2011-09-01
      • 1970-01-01
      • 2022-07-23
      相关资源
      最近更新 更多