【问题标题】:How To: Execute command line in C#, get STD OUT results如何:在 C# 中执行命令行,获取 STD OUT 结果
【发布时间】:2010-09-17 09:44:15
【问题描述】:

如何从 C# 执行命令行程序并取回 STD OUT 结果?具体来说,我想对以编程方式选择的两个文件执行 DIFF,并将结果写入文本框。

【问题讨论】:

标签: c# command-line


【解决方案1】:
// Start the child process.
 Process p = new Process();
 // Redirect the output stream of the child process.
 p.StartInfo.UseShellExecute = false;
 p.StartInfo.RedirectStandardOutput = true;
 p.StartInfo.FileName = "YOURBATCHFILE.bat";
 p.Start();
 // Do not wait for the child process to exit before
 // reading to the end of its redirected stream.
 // p.WaitForExit();
 // Read the output stream first and then wait.
 string output = p.StandardOutput.ReadToEnd();
 p.WaitForExit();

代码来自MSDN

【讨论】:

  • 有没有办法在没有批处理文件的情况下做到这一点?问题是,我需要向命令发送一些参数。我正在使用 xsd.exe /type:,所以我需要能够同时设置 Assembly 和 ClassName,然后运行命令。
  • 您可以通过{YourProcessObject}.StartInfo.Arguments 字符串为您的调用添加参数。
  • 如何让进程以管理员身份运行?
  • 我遇到了许多问题,我的进程使用此代码完全停止,因为该进程已将足够的数据写入p.StandardError 流。当流变满时,似乎进程会停止,直到数据被消耗完,所以我必须同时读取StandardErrorStandardOutput 以保证任务正确执行。
  • c# 编译器的快速提示:Process 对象必须将 UseShellExecute 属性设置为 false 才能重定向 IO 流。
【解决方案2】:

这是一个简单的示例:

//Create process
System.Diagnostics.Process pProcess = new System.Diagnostics.Process();

//strCommand is path and file name of command to run
pProcess.StartInfo.FileName = strCommand;

//strCommandParameters are parameters to pass to program
pProcess.StartInfo.Arguments = strCommandParameters;

pProcess.StartInfo.UseShellExecute = false;

//Set output of program to be written to process output stream
pProcess.StartInfo.RedirectStandardOutput = true;   

//Optional
pProcess.StartInfo.WorkingDirectory = strWorkingDirectory;

//Start the process
pProcess.Start();

//Get program output
string strOutput = pProcess.StandardOutput.ReadToEnd();

//Wait for process to finish
pProcess.WaitForExit();

【讨论】:

  • +1 用于展示如何添加参数来运行命令行程序(接受的答案没有。)
【解决方案3】:

我发现另一个有用的参数,我用它来消除进程窗口

pProcess.StartInfo.CreateNoWindow = true;

如果这是您想要的,这有助于完全隐藏用户的黑色控制台窗口。

【讨论】:

  • 帮我省了很多麻烦。谢谢。
  • 调用“sc”时,我还必须设置 StartInfo.WindowStyle = ProcessWindowStyle.Hidden。
【解决方案4】:
// usage
const string ToolFileName = "example.exe";
string output = RunExternalExe(ToolFileName);

public string RunExternalExe(string filename, string arguments = null)
{
    var process = new Process();

    process.StartInfo.FileName = filename;
    if (!string.IsNullOrEmpty(arguments))
    {
        process.StartInfo.Arguments = arguments;
    }

    process.StartInfo.CreateNoWindow = true;
    process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
    process.StartInfo.UseShellExecute = false;

    process.StartInfo.RedirectStandardError = true;
    process.StartInfo.RedirectStandardOutput = true;
    var stdOutput = new StringBuilder();
    process.OutputDataReceived += (sender, args) => stdOutput.AppendLine(args.Data); // Use AppendLine rather than Append since args.Data is one line of output, not including the newline character.

    string stdError = null;
    try
    {
        process.Start();
        process.BeginOutputReadLine();
        stdError = process.StandardError.ReadToEnd();
        process.WaitForExit();
    }
    catch (Exception e)
    {
        throw new Exception("OS error while executing " + Format(filename, arguments)+ ": " + e.Message, e);
    }

    if (process.ExitCode == 0)
    {
        return stdOutput.ToString();
    }
    else
    {
        var message = new StringBuilder();

        if (!string.IsNullOrEmpty(stdError))
        {
            message.AppendLine(stdError);
        }

        if (stdOutput.Length != 0)
        {
            message.AppendLine("Std output:");
            message.AppendLine(stdOutput.ToString());
        }

        throw new Exception(Format(filename, arguments) + " finished with exit code = " + process.ExitCode + ": " + message);
    }
}

private string Format(string filename, string arguments)
{
    return "'" + filename + 
        ((string.IsNullOrEmpty(arguments)) ? string.Empty : " " + arguments) +
        "'";
}

【讨论】:

  • 一个非常全面的例子,谢谢
  • 可能想要将 OutputDataReceived 处理程序更改为 stdOut.AppendLine()
  • 在我看来,这是一个比公认的答案更全面的解决方案。我现在在用,还没有用过接受的那个,但是那个看起来真的很缺。
  • 感谢process.StartInfo.RedirectStandardError = true;if (process.ExitCode == 0) 接受的答案没有。
  • 谢谢,它适用于 Ubuntu 20 LTS。 (仅作记录)
【解决方案5】:

此页面上接受的答案有一个弱点,在极少数情况下会很麻烦。按照惯例,程序写入的文件句柄有两个,stdout 和 stderr。 如果您只是读取单个文件句柄,例如来自 Ray 的答案,并且您正在启动的程序将足够的输出写入 stderr,它将填满输出 stderr 缓冲区并阻塞。然后你的两个进程就死锁了。缓冲区大小可以是 4K。 这在短期程序中极为罕见,但如果您有一个长期运行的程序反复输出到 stderr,它最终会发生。这很难调试和跟踪。

有几个很好的方法可以解决这个问题。

  1. 一种方法是执行 cmd.exe 而不是您的程序,并使用 cmd.exe 的 /c 参数来调用您的程序以及 cmd.exe 的“2>&1”参数来告诉它合并 stdout 和 stderr .

            var p = new Process();
            p.StartInfo.FileName = "cmd.exe";
            p.StartInfo.Arguments = "/c mycmd.exe 2>&1";
    
  2. 另一种方法是使用同时读取两个句柄的编程模型。

            var p = new Process();
            p.StartInfo.FileName = "cmd.exe";
            p.StartInfo.Arguments = @"/c dir \windows";
            p.StartInfo.CreateNoWindow = true;
            p.StartInfo.RedirectStandardError = true;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.RedirectStandardInput = false;
            p.OutputDataReceived += (a, b) => Console.WriteLine(b.Data);
            p.ErrorDataReceived += (a, b) => Console.WriteLine(b.Data);
            p.Start();
            p.BeginErrorReadLine();
            p.BeginOutputReadLine();
            p.WaitForExit();
    

【讨论】:

  • 我认为这更好地回答了原始问题,因为它显示了如何通过 C#(不是文件)运行 CMD 命令。
【解决方案6】:
 System.Diagnostics.ProcessStartInfo psi =
   new System.Diagnostics.ProcessStartInfo(@"program_to_call.exe");
 psi.RedirectStandardOutput = true;
 psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
 psi.UseShellExecute = false;
 System.Diagnostics.Process proc = System.Diagnostics.Process.Start(psi); ////
 System.IO.StreamReader myOutput = proc.StandardOutput;
 proc.WaitForExit(2000);
 if (proc.HasExited)
  {
      string output = myOutput.ReadToEnd();
 }

【讨论】:

  • 进程写入大量数据时可能会死锁。最好在进程仍在运行时开始读取数据。
【解决方案7】:

如果您不介意引入依赖项,CliWrap 可以为您简化:

using CliWrap;
using CliWrap.Buffered;

var result = await Cli.Wrap("target.exe")
   .WithArguments("arguments")
   .ExecuteBufferedAsync();

var stdout = result.StandardOutput;

【讨论】:

    【解决方案8】:

    您需要使用ProcessStartInfo 并启用RedirectStandardOutput - 然后您可以读取输出流。您可能会发现使用“>”将输出重定向到文件(通过操作系统)更容易,然后只需读取文件。

    [编辑:就像雷所做的那样:+1]

    【讨论】:

    • 这会迫使您在需要权限的地方写入文件,需要找到位置和名称,并且在完成后不要忘记删除。实际上更容易使用RedirectStandardOutput
    【解决方案9】:

    单行运行命令:

    new Process() { StartInfo = new ProcessStartInfo("echo", "Hello, World") }.Start();
    

    以最短的可重复代码读取命令输出:

        var cliProcess = new Process() {
            StartInfo = new ProcessStartInfo("echo", "Hello, World") {
                UseShellExecute = false,
                RedirectStandardOutput = true
            }
        };
        cliProcess.Start();
        string cliOut = cliProcess.StandardOutput.ReadToEnd();
        cliProcess.WaitForExit();
        cliProcess.Close();
    

    【讨论】:

      【解决方案10】:

      您可以使用 Process 类启动任何命令行程序,并使用您创建的流读取器(基于字符串或内存位置)设置 Process 实例的 StandardOutput 属性。该过程完成后,您可以在该流上执行所需的任何差异。

      【讨论】:

        【解决方案11】:

        如果您尝试查询 PC/服务器上的本地 ARP 缓存,这可能对某人有用。

        List<string[]> results = new List<string[]>();
        
                using (Process p = new Process())
                {
                    p.StartInfo.CreateNoWindow = true;
                    p.StartInfo.RedirectStandardOutput = true;
                    p.StartInfo.UseShellExecute = false;
                    p.StartInfo.Arguments = "/c arp -a";
                    p.StartInfo.FileName = @"C:\Windows\System32\cmd.exe";
                    p.Start();
        
                    string line;
        
                    while ((line = p.StandardOutput.ReadLine()) != null)
                    {
                        if (line != "" && !line.Contains("Interface") && !line.Contains("Physical Address"))
                        {
                            var lineArr = line.Trim().Split(' ').Select(n => n).Where(n => !string.IsNullOrEmpty(n)).ToArray();
                            var arrResult = new string[]
                        {
                           lineArr[0],
                           lineArr[1],
                           lineArr[2]
                        };
                            results.Add(arrResult);
                        }
                    }
        
                    p.WaitForExit();
                }
        

        【讨论】:

          【解决方案12】:

          如果您还需要在 cmd.exe 中执行某些命令,您可以执行以下操作:

          // Start the child process.
          Process p = new Process();
          // Redirect the output stream of the child process.
          p.StartInfo.UseShellExecute = false;
          p.StartInfo.RedirectStandardOutput = true;
          p.StartInfo.FileName = "cmd.exe";
          p.StartInfo.Arguments = "/C vol";
          p.Start();
          // Read the output stream first and then wait.
          string output = p.StandardOutput.ReadToEnd();
          p.WaitForExit();
          Console.WriteLine(output);
          

          这仅返回命令本身的输出:

          您也可以使用StandardInput 代替StartInfo.Arguments

          // Start the child process.
          Process p = new Process();
          // Redirect the output stream of the child process.
          p.StartInfo.UseShellExecute = false;
          p.StartInfo.RedirectStandardInput = true;
          p.StartInfo.RedirectStandardOutput = true;
          p.StartInfo.FileName = "cmd.exe";
          p.Start();
          // Read the output stream first and then wait.
          p.StandardInput.WriteLine("vol");
          p.StandardInput.WriteLine("exit");
          string output = p.StandardOutput.ReadToEnd();
          p.WaitForExit();
          Console.WriteLine(output);
          

          结果如下:

          【讨论】:

            【解决方案13】:

            由于这里的大多数答案都没有为IDisposable 实现using statemant 以及我认为可能需要的其他一些东西,所以我会添加这个答案。

            对于 C# 8.0

            // Start a process with the filename or path with filename e.g. "cmd". Please note the 
            //using statemant
            using myProcess.StartInfo.FileName = "cmd";
            // add the arguments - Note add "/c" if you want to carry out tge  argument in cmd and  
            // terminate
            myProcess.StartInfo.Arguments = "/c dir";
            // Allows to raise events
            myProcess.EnableRaisingEvents = true;
            //hosted by the application itself to not open a black cmd window
            myProcess.StartInfo.UseShellExecute = false;
            myProcess.StartInfo.CreateNoWindow = true;
            // Eventhander for data
            myProcess.Exited += OnOutputDataRecived;
            // Eventhandler for error
            myProcess.ErrorDataReceived += OnErrorDataReceived;
            // Eventhandler wich fires when exited
            myProcess.Exited += OnExited;
            // Starts the process
            myProcess.Start();
            //read the output before you wait for exit
            myProcess.BeginOutputReadLine();
            // wait for the finish - this will block (leave this out if you dont want to wait for 
            // it, so it runs without blocking)
            process.WaitForExit();
            
            // Handle the dataevent
            private void OnOutputDataRecived(object sender, DataReceivedEventArgs e)
            {
                //do something with your data
                Trace.WriteLine(e.Data);
            }
            
            //Handle the error
            private void OnErrorDataReceived(object sender, DataReceivedEventArgs e)
            {        
                Trace.WriteLine(e.Data);
                //do something with your exception
                throw new Exception();
            }    
            
            // Handle Exited event and display process information.
            private void OnExited(object sender, System.EventArgs e)
            {
                 Trace.WriteLine("Process exited");
            }
            

            【讨论】:

              【解决方案14】:

              这可能不是最好/最简单的方法,但可能是一种选择:

              从代码执行时,添加“ > output.txt”,然后读入 output.txt 文件。

              【讨论】:

              • 你假设你有写权限,这可能不是真的
              【解决方案15】:

              PublicDomain 开源代码中有一个 ProcessHelper 类,您可能会感兴趣。

              【讨论】:

                【解决方案16】:

                Julian 的解决方案已经过测试,并进行了一些小的修正。以下是同样使用https://sourceforge.net/projects/bat-to-exe/GenericConsole.cs 和https://www.codeproject.com/Articles/19225/Bat-file-compilerprogram.txt 作为args 部分的示例:

                using System;
                using System.Text;  //StringBuilder
                using System.Diagnostics;
                using System.IO;
                
                
                class Program
                {
                    private static bool redirectStandardOutput = true;
                
                    private static string buildargument(string[] args)
                    {
                        StringBuilder arg = new StringBuilder();
                        for (int i = 0; i < args.Length; i++)
                        {
                            arg.Append("\"" + args[i] + "\" ");
                        }
                
                        return arg.ToString();
                    }
                
                    static void Main(string[] args)
                    {
                        Process prc = new Process();
                        prc.StartInfo = //new ProcessStartInfo("cmd.exe", String.Format("/c \"\"{0}\" {1}", Path.Combine(Environment.CurrentDirectory, "mapTargetIDToTargetNameA3.bat"), buildargument(args)));
                        //new ProcessStartInfo(Path.Combine(Environment.CurrentDirectory, "mapTargetIDToTargetNameA3.bat"), buildargument(args));
                        new ProcessStartInfo("mapTargetIDToTargetNameA3.bat");
                        prc.StartInfo.Arguments = buildargument(args);
                
                        prc.EnableRaisingEvents = true;
                
                        if (redirectStandardOutput == true)
                        {
                            prc.StartInfo.UseShellExecute = false;
                        }
                        else
                        {
                            prc.StartInfo.UseShellExecute = true;
                        }
                
                        prc.StartInfo.CreateNoWindow = true;
                
                        prc.OutputDataReceived += OnOutputDataRecived;
                        prc.ErrorDataReceived += OnErrorDataReceived;
                        //prc.Exited += OnExited;
                
                        prc.StartInfo.RedirectStandardOutput = redirectStandardOutput;
                        prc.StartInfo.RedirectStandardError = redirectStandardOutput;
                
                        try
                        {
                            prc.Start();
                            prc.BeginOutputReadLine();
                            prc.BeginErrorReadLine();
                            prc.WaitForExit();
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine("OS error: " + e.Message);
                        }
                
                        prc.Close();
                    }
                
                    // Handle the dataevent
                    private static void OnOutputDataRecived(object sender, DataReceivedEventArgs e)
                    {
                        //do something with your data
                        Console.WriteLine(e.Data);
                    }
                
                    //Handle the error
                    private static void OnErrorDataReceived(object sender, DataReceivedEventArgs e)
                    {
                        Console.WriteLine(e.Data);
                    }
                
                    // Handle Exited event and display process information.
                    //private static void OnExited(object sender, System.EventArgs e)
                    //{
                    //    var process = sender as Process;
                    //    if (process != null)
                    //    {
                    //        Console.WriteLine("ExitCode: " + process.ExitCode);
                    //    }
                    //    else
                    //    {
                    //        Console.WriteLine("Process exited");
                    //    }
                    //}
                }
                

                代码需要在 VS2007 中编译,使用命令行 csc.exe 生成的可执行文件将无法正确显示控制台输出,甚至会因 CLR20r3 错误而崩溃。注释掉OnExited事件进程,bat到exe的console输出会更像原来的bat控制台输出。

                【讨论】:

                  【解决方案17】:

                  这是一个小例子:

                  using System;
                  using System.Diagnostics;
                  
                  class Program
                  {
                      static void Main(string[] args)
                      {
                          var p = Process.Start(
                              new ProcessStartInfo("git", "branch --show-current")
                              {
                                  CreateNoWindow = true,
                                  UseShellExecute = false,
                                  RedirectStandardError = true,
                                  RedirectStandardOutput = true,
                                  WorkingDirectory = Environment.CurrentDirectory
                              }
                          );
                  
                          p.WaitForExit();
                          string branchName =p.StandardOutput.ReadToEnd().TrimEnd();
                          string errorInfoIfAny =p.StandardError.ReadToEnd().TrimEnd();
                  
                          if (errorInfoIfAny.Length != 0)
                          {
                              Console.WriteLine($"error: {errorInfoIfAny}");
                          }
                          else { 
                              Console.WriteLine($"branch: {branchName}");
                          }
                  
                      }
                  }
                  

                  我相信这是最短的形式。

                  请注意,大多数命令行工具很容易混淆标准输出和标准错误,有时只需将它们组合成一个字符串就有意义。

                  p.ExitCode 有时也可能有用。

                  【讨论】:

                    【解决方案18】:

                    只是为了好玩,这是我获得 PYTHON 输出的完整解决方案 - 在按钮单击下 - 带有错误报告。只需添加一个名为“butPython”的按钮和一个名为“llHello”的标签...

                        private void butPython(object sender, EventArgs e)
                        {
                            llHello.Text = "Calling Python...";
                            this.Refresh();
                            Tuple<String,String> python = GoPython(@"C:\Users\BLAH\Desktop\Code\Python\BLAH.py");
                            llHello.Text = python.Item1; // Show result.
                            if (python.Item2.Length > 0) MessageBox.Show("Sorry, there was an error:" + Environment.NewLine + python.Item2);
                        }
                    
                        public Tuple<String,String> GoPython(string pythonFile, string moreArgs = "")
                        {
                            ProcessStartInfo PSI = new ProcessStartInfo();
                            PSI.FileName = "py.exe";
                            PSI.Arguments = string.Format("\"{0}\" {1}", pythonFile, moreArgs);
                            PSI.CreateNoWindow = true;
                            PSI.UseShellExecute = false;
                            PSI.RedirectStandardError = true;
                            PSI.RedirectStandardOutput = true;
                            using (Process process = Process.Start(PSI))
                                using (StreamReader reader = process.StandardOutput)
                                {
                                    string stderr = process.StandardError.ReadToEnd(); // Error(s)!!
                                    string result = reader.ReadToEnd(); // What we want.
                                    return new Tuple<String,String> (result,stderr); 
                                }
                        }
                    

                    【讨论】:

                      猜你喜欢
                      • 2016-03-21
                      • 1970-01-01
                      • 2015-07-06
                      • 1970-01-01
                      • 2020-10-21
                      • 2011-11-03
                      • 2012-02-10
                      • 2011-10-30
                      相关资源
                      最近更新 更多