C# 如何在 .NET 中生成进程并捕获其 STDOUT?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/285760/
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 spawn a process and capture its STDOUT in .NET?
提问by FlySwat
I need to spawn a child process that is a console application, and capture its output.
我需要生成一个作为控制台应用程序的子进程,并捕获其输出。
I wrote up the following code for a method:
我为一个方法编写了以下代码:
string retMessage = String.Empty;
ProcessStartInfo startInfo = new ProcessStartInfo();
Process p = new Process();
startInfo.CreateNoWindow = true;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardInput = true;
startInfo.UseShellExecute = false;
startInfo.Arguments = command;
startInfo.FileName = exec;
p.StartInfo = startInfo;
p.Start();
p.OutputDataReceived += new DataReceivedEventHandler
(
delegate(object sender, DataReceivedEventArgs e)
{
using (StreamReader output = p.StandardOutput)
{
retMessage = output.ReadToEnd();
}
}
);
p.WaitForExit();
return retMessage;
However, this does not return anything. I don't believe the OutputDataReceived
event is being called back, or the WaitForExit()
command may be blocking the thread so it will never callback.
但是,这不会返回任何内容。我不相信该OutputDataReceived
事件正在被回调,或者该WaitForExit()
命令可能会阻塞线程,因此它永远不会回调。
Any advice?
有什么建议吗?
EDIT:Looks like I was trying too hard with the callback. Doing:
编辑:看起来我对回调太努力了。正在做:
return p.StandardOutput.ReadToEnd();
Appears to work fine.
似乎工作正常。
回答by SoapBox
You need to call p.Start() to actually run the process after you set the StartInfo. As it is, your function is probably hanging on the WaitForExit() call because the process was never actually started.
设置 StartInfo 后,您需要调用 p.Start() 来实际运行该进程。实际上,您的函数可能挂在 WaitForExit() 调用上,因为该进程从未真正启动。
回答by Judah Gabriel Himango
Here's code that I've verified to work. I use it for spawning MSBuild and listening to its output:
这是我已经验证可以工作的代码。我用它来生成 MSBuild 并收听它的输出:
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += (sender, args) => Console.WriteLine("received output: {0}", args.Data);
process.Start();
process.BeginOutputReadLine();
回答by JaredPar
It looks like two of your lines are out of order. You start the process before setting up an event handler to capture the output. It's possible the process is just finishing before the event handler is added.
看起来您的两条线路出现故障。您在设置事件处理程序以捕获输出之前启动该过程。在添加事件处理程序之前,该过程可能刚刚完成。
Switch the lines like so.
像这样切换线路。
p.OutputDataReceived += ...
p.Start();
回答by Umar Farooq Khawaja
I just tried this very thing and the following worked for me:
我刚刚尝试过这件事,以下对我有用:
StringBuilder outputBuilder;
ProcessStartInfo processStartInfo;
Process process;
outputBuilder = new StringBuilder();
processStartInfo = new ProcessStartInfo();
processStartInfo.CreateNoWindow = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardInput = true;
processStartInfo.UseShellExecute = false;
processStartInfo.Arguments = "<insert command line arguments here>";
processStartInfo.FileName = "<insert tool path here>";
process = new Process();
process.StartInfo = processStartInfo;
// enable raising events because Process does not raise events by default
process.EnableRaisingEvents = true;
// attach the event handler for OutputDataReceived before starting the process
process.OutputDataReceived += new DataReceivedEventHandler
(
delegate(object sender, DataReceivedEventArgs e)
{
// append the new data to the data already read-in
outputBuilder.Append(e.Data);
}
);
// start the process
// then begin asynchronously reading the output
// then wait for the process to exit
// then cancel asynchronously reading the output
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
process.CancelOutputRead();
// use the output
string output = outputBuilder.ToString();
回答by Beatles1692
Here's a method that I use to run a process and gets its output and errors :
这是我用来运行进程并获取其输出和错误的方法:
public static string ShellExecute(this string path, string command, TextWriter writer, params string[] arguments)
{
using (var process = Process.Start(new ProcessStartInfo { WorkingDirectory = path, FileName = command, Arguments = string.Join(" ", arguments), UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true }))
{
using (process.StandardOutput)
{
writer.WriteLine(process.StandardOutput.ReadToEnd());
}
using (process.StandardError)
{
writer.WriteLine(process.StandardError.ReadToEnd());
}
}
return path;
}
For example :
例如 :
@"E:\Temp\MyWorkingDirectory".ShellExecute(@"C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\svcutil.exe", Console.Out);
回答by Sam
Here's some full and simple code to do this. This worked fine when I used it.
这是一些完整而简单的代码来做到这一点。当我使用它时,这很好用。
var processStartInfo = new ProcessStartInfo
{
FileName = @"C:\SomeProgram",
Arguments = "Arguments",
RedirectStandardOutput = true,
UseShellExecute = false
};
var process = Process.Start(processStartInfo);
var output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
Note that this only captures standard output; it doesn't capture standard error. If you want both, use this techniquefor each stream.
请注意,这仅捕获标准输出;它不捕获标准错误。如果两者都需要,请对每个流使用此技术。
回答by jws
Redirecting the stream is asynchronous and will potentially continue after the process has terminated. It is mentioned by Umar to cancel after process termination process.CancelOutputRead()
. However that has data loss potential.
重定向流是异步的,并且可能会在进程终止后继续。Umar 提到在进程终止后取消process.CancelOutputRead()
。然而,这有数据丢失的可能性。
This is working reliably for me:
这对我来说很可靠:
process.WaitForExit(...);
...
while (process.StandardOutput.EndOfStream == false)
{
Thread.Sleep(100);
}
I didn't try this approach but I like the suggestion from Sly:
我没有尝试这种方法,但我喜欢 Sly 的建议:
if (process.WaitForExit(timeout))
{
process.WaitForExit();
}
回答by Robb Sadler
I needed to capture both stdout and stderr and have it timeout if the process didn't exit when expected. I came up with this:
我需要同时捕获 stdout 和 stderr 并在进程未按预期退出时使其超时。我想出了这个:
Process process = new Process();
StringBuilder outputStringBuilder = new StringBuilder();
try
{
process.StartInfo.FileName = exeFileName;
process.StartInfo.WorkingDirectory = args.ExeDirectory;
process.StartInfo.Arguments = args;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.EnableRaisingEvents = false;
process.OutputDataReceived += (sender, eventArgs) => outputStringBuilder.AppendLine(eventArgs.Data);
process.ErrorDataReceived += (sender, eventArgs) => outputStringBuilder.AppendLine(eventArgs.Data);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
var processExited = process.WaitForExit(PROCESS_TIMEOUT);
if (processExited == false) // we timed out...
{
process.Kill();
throw new Exception("ERROR: Process took too long to finish");
}
else if (process.ExitCode != 0)
{
var output = outputStringBuilder.ToString();
var prefixMessage = "";
throw new Exception("Process exited with non-zero exit code of: " + process.ExitCode + Environment.NewLine +
"Output from process: " + outputStringBuilder.ToString());
}
}
finally
{
process.Close();
}
I am piping the stdout and stderr into the same string, but you could keep it separate if needed. It uses events, so it should handle them as they come (I believe). I have run this successfully, and will be volume testing it soon.
我将 stdout 和 stderr 管道化到同一个字符串中,但如果需要,您可以将它们分开。它使用事件,所以它应该在它们到来时处理它们(我相信)。我已经成功运行了它,并将很快对其进行批量测试。
回答by Craig
The answer from Judah did not work for me (or is not complete) as the application was exiting after the first BeginOutputReadLine();
Judah 的回答对我不起作用(或不完整),因为应用程序在第一个之后退出 BeginOutputReadLine();
This works for me as a complete snippet, reading the constant output of a ping:
这对我来说是一个完整的片段,读取 ping 的恒定输出:
var process = new Process();
process.StartInfo.FileName = "ping";
process.StartInfo.Arguments = "google.com -t";
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UseShellExecute = false;
process.OutputDataReceived += (sender, a) => Console.WriteLine(a.Data);
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();