C# 如何一次异步读取标准输出流和标准错误流
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12566166/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to asynchronously read the standard output stream and standard error stream at once
提问by Martin Vseticka
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 behaviour of console (the blending of stdout and stderr).
然后我可以读取标准输出(我可以对标准错误执行相同的操作)但我不知道如何模拟控制台的行为(stdout 和 stderr 的混合)。
Note: I know that Linux has the feature of redirecting standard error stream to the standard output stream but I was unable to google it for .NET.
注意:我知道 Linux 具有将标准错误流重定向到标准输出流的功能,但我无法在 .NET 上搜索它。
I must miss something pretty easy.
我一定会错过一些很容易的事情。
Thanks!
谢谢!
采纳答案by Martin Vseticka
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
回答by Serge
The MSDN articlestates:
MSDN文章指出:
The redirected StandardError stream can be read synchronously or asynchronously. Methods such as Read, ReadLine, and ReadToEnd perform synchronous read operations on the error output stream of the process. These synchronous read operations do not complete until the associated Process writes to its StandardError stream, or closes the stream.
In contrast, BeginErrorReadLine starts asynchronous read operations on the StandardError stream. 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. 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.
可以同步或异步读取重定向的 StandardError 流。Read、ReadLine、ReadToEnd等方法对进程的错误输出流进行同步读操作。这些同步读取操作直到关联的进程写入其 StandardError 流或关闭流时才会完成。
相比之下,BeginErrorReadLine 在 StandardError 流上启动异步读取操作。此方法为流输出启用指定的事件处理程序并立即返回给调用者,调用者可以在将流输出定向到事件处理程序的同时执行其他工作。
同步读取操作在调用者从 StandardError 流读取和子进程写入该流之间引入了依赖关系。这些依赖性会导致死锁情况。当调用者从子进程的重定向流中读取时,它依赖于子进程。调用者等待读取操作,直到孩子写入流或关闭流。当子进程写入足够的数据来填充其重定向流时,它依赖于父进程。子进程等待下一个写操作,直到父进程从完整的流中读取或关闭流。当调用者和子进程相互等待以完成操作,并且都不能继续时,就会导致死锁条件。
The same applies to the StandardOutput, so you just read both streams asynchronously.
这同样适用于StandardOutput,因此您只需异步读取两个流。
Mergingboth streams into one complicates detection of what output is the error reporting and what is the 'product' information.
Merging两个流合二为一使检测什么输出是错误报告和什么是“产品”信息变得复杂。
回答by Ohad Schneider
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);
}
回答by TarmoPikaro
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;
}

