简体   繁体   English

如何一次异步读取标准输出流和标准错误流

[英]How to asynchronously read the standard output stream and standard error stream at once

I want to read the ouput of a process in the form as is in a console (standard output is blended with standard error in one stream).我想以控制台中的形式读取进程的输出(标准输出与一个流中的标准错误混合)。 Is there a way how to do it?有没有办法做到这一点?

I was thinking about using我正在考虑使用

ProcessStartInfo.UseShellExecute = true;  

but then I cannot read asynchronously the output.但后来我无法异步读取输出。 If I set如果我设置

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

then I can read standard output (I can do the same for standard error) but I don't know how to simulate the behavior of console (the blending of stdout and stderr).然后我可以读取标准输出(我可以对标准错误执行相同的操作)但我不知道如何模拟控制台的行为(stdout 和 stderr 的混合)。

This is similar to Linux which has the feature of redirecting standard error stream to the standard output stream;这类似于Linux 具有将标准错误流重定向到标准输出流的特性; how?如何?

Do you mean something like this?你的意思是这样的吗?

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);
}

I found the answer:我找到了答案:

The output streams are buffered.输出流被缓冲。 There is no way to get the true sequential order of the items inserted into the streams.无法获得插入到流中的项目的真正顺序。 In fact it makes little sense as both streams can be written too at the same time.事实上,这没什么意义,因为两个流也可以同时写入。 They are independent of each other.它们彼此独立。 Therefore the best you can do is get the sequential output from each one as they arrive.因此,您能做的最好的事情就是在每个人到达时获得它们的顺序输出。

Generally this is not an issue though as almost all console apps use standard output for both output and error messages.通常这不是问题,因为几乎所有控制台应用程序都使用标准输出来输出和错误消息。 The error stream is used by some apps but the messages are generally duplicates of the errors generated in the output stream.某些应用程序使用错误流,但消息通常是输出流中生成的错误的副本。

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

The MSDN article states: MSDN 文章指出:

The redirected StandardError stream can be read synchronously or asynchronously.可以同步或异步读取重定向的 StandardError 流。 Methods such as Read, ReadLine, and ReadToEnd perform synchronous read operations on the error output stream of the process. Read、ReadLine、ReadToEnd等方法对进程的错误输出流进行同步读操作。 These synchronous read operations do not complete until the associated Process writes to its StandardError stream, or closes the stream.这些同步读取操作直到关联的进程写入其 StandardError 流或关闭流时才会完成。

In contrast, BeginErrorReadLine starts asynchronous read operations on the StandardError stream.相比之下,BeginErrorReadLine 在 StandardError 流上启动异步读取操作。 This method enables a designated event handler for the stream output and immediately returns to the caller, which can perform other work while the stream output is directed to the event handler.此方法为流输出启用指定的事件处理程序并立即返回给调用者,调用者可以在将流输出定向到事件处理程序的同时执行其他工作。

Synchronous read operations introduce a dependency between the caller reading from the StandardError stream and the child process writing to that stream.同步读取操作在调用者从 StandardError 流读取和子进程写入该流之间引入了依赖关系。 These dependencies can result in deadlock conditions.这些依赖性会导致死锁情况。 When the caller reads from the redirected stream of a child process, it is dependent on the child.当调用者从子进程的重定向流中读取时,它依赖于子进程。 The caller waits on the read operation until the child writes to the stream or closes the stream.调用者等待读取操作,直到孩子写入流或关闭流。 When the child process writes enough data to fill its redirected stream, it is dependent on the parent.当子进程写入足够的数据来填充其重定向流时,它依赖于父进程。 The child process waits on the next write operation until the parent reads from the full stream or closes the stream.子进程等待下一个写操作,直到父进程从完整的流中读取或关闭流。 The deadlock condition results when the caller and child process wait on each other to complete an operation, and neither can proceed.当调用者和子进程相互等待以完成操作,并且都不能继续时,就会导致死锁条件。 You can avoid deadlocks by evaluating dependencies between the caller and child process.您可以通过评估调用者和子进程之间的依赖关系来避免死锁。

The same applies to the StandardOutput , so you just read both streams asynchronously.这同样适用于StandardOutput ,因此您只需异步读取两个流。

Merging both streams into one complicates detection of what output is the error reporting and what is the 'product' information. Merging两个流Merging为一个流会使检测什么输出是错误报告以及什么是“产品”信息变得复杂。

Similar kind of example, with exception that I'm collecting stdout and error into separate strings using StringBuilder for that purpose.类似的例子,除了我为此目的使用 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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM