如何:在c_中执行命令行,获取std out结果

How To: Execute command line in C#, get STD OUT results

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


1
2
3
4
5
6
7
8
9
10
11
12
13
// 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。


下面是一个快速的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//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
pProcess.StartInfo.CreateNoWindow = true;

这有助于对用户完全隐藏黑色控制台窗口(如果您希望这样做的话)。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// 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) +
       "'";
}


1
2
3
4
5
6
7
8
9
10
11
12
 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();
 }

本页接受的答案有一个弱点,在极少数情况下很麻烦。有两个文件句柄,程序按约定写入,stdout和stderr。如果您只是读取一个文件句柄(如来自ray的应答),并且您正在启动的程序将足够的输出写入stderr,那么它将填充输出stderr缓冲区和块。然后您的两个进程就死锁了。缓冲区大小可以是4K。这在短期程序中非常罕见,但是如果有一个长时间运行的程序反复输出到stderr,它最终会发生。这很难调试和跟踪。

有两种很好的方法来解决这个问题。

  • 一种方法是执行cmd.exe而不是您的程序,并使用cmd.exe的/c参数来调用您的程序,同时使用cmd.exe的"2>&;1"参数来告诉它合并stdout和stderr。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
            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();

  • 您需要在启用RedirectStandardOutput的情况下使用ProcessStartInfo,然后才能读取输出流。您可能会发现使用">"将输出重定向到一个文件(通过操作系统),然后简单地读取该文件更容易。

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


    如果您不介意引入依赖项,cliwrap可以为您简化这一过程:

    1
    2
    3
    var cli = new Cli("target.exe");
    var output = await cli.ExecuteAsync("arguments","stdin");
    var stdout = output.StandardOutput;

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    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();
            }

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


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

    从代码执行时,添加">output.txt",然后读取output.txt文件。


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


    为了好玩,这里是我完成的关于获取python输出的解决方案-在按钮点击下-带有错误报告。只需添加一个名为"Butpython"的按钮和一个名为"llhello"的标签…

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
        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);
                }
        }