Cách thực hiện: Thực thi dòng lệnh trong C #, nhận kết quả STD OUT


472

Làm cách nào để tôi thực hiện chương trình dòng lệnh từ C # và lấy lại kết quả STD OUT? Cụ thể, tôi muốn thực thi DIFF trên hai tệp được chọn theo chương trình và ghi kết quả vào hộp văn bản.


2
Xem thêm stackoverflow.com/a/5367686/492 - nó hiển thị các sự kiện cho đầu ra và lỗi.
CAD bloke

Liên quan (nhưng không bắt STDOUT): stackoverflow.com/questions/1469764
user202729

Câu trả lời:


523
// 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();

Mã là từ MSDN .


8
Có cách nào để làm điều này mà không cần một tập tin bó? Điều đó là, tôi cần gửi một số tham số cho lệnh. Tôi đang sử dụng xsd.exe <hội> / type: <ClassName>, vì vậy tôi cần có khả năng đặt cả hội và ClassName, sau đó chạy lệnh.
Carlo

26
Bạn có thể thêm đối số vào cuộc gọi của bạn thông qua {YourProcessObject}.StartInfo.Argumentschuỗi.
patridge

5
Làm thế nào để làm cho quá trình chạy như quản trị viên?
Saher Ahwal

5
Tôi đã gặp một số vấn đề trong đó quy trình của tôi, sử dụng mã này, tạm dừng hoàn toàn vì quy trình đã ghi đủ dữ liệu vào p.StandardErrorluồng. Khi luồng đầy, có vẻ như quá trình sẽ tạm dừng cho đến khi dữ liệu được tiêu thụ, vì vậy tôi phải đọc cả hai StandardErrorStandardOutputđể đảm bảo rằng một tác vụ thực thi chính xác.
Ted Spence

5
Headup nhanh từ trình biên dịch c #: Đối tượng Process phải có thuộc tính UseShellExecute được đặt thành false để chuyển hướng các luồng IO.
IbrarMumtaz

144

Đây là một mẫu nhanh:

//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();

2
+1 để hiển thị cách thêm đối số để chạy chương trình dòng lệnh (câu trả lời được chấp nhận không có.)
Suman

104

Có một tham số khác tôi thấy hữu ích, mà tôi sử dụng để loại bỏ cửa sổ quy trình

pProcess.StartInfo.CreateNoWindow = true;

Điều này giúp ẩn hoàn toàn cửa sổ giao diện điều khiển màu đen khỏi người dùng, nếu đó là những gì bạn mong muốn.


3
Cứu tôi rất nhiều đau đầu. Cảm ơn.
Vivandiere

2
Khi gọi "sc", tôi cũng phải đặt StartInfo.WindowStyle = ProcessWindowStyle.Hidden.
Pedro

90
// 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) +
        "'";
}

3
Một ví dụ rất toàn diện, Cảm ơn
ShahidAzim

2
Có thể muốn thay đổi trình xử lý OutputDataReceured thành stdOut.AppendLine ()
Paul Williams

3
Theo tôi, đây là một giải pháp toàn diện hơn nhiều so với câu trả lời được chấp nhận. Tôi hiện đang sử dụng nó và chưa sử dụng cái được chấp nhận, nhưng cái đó thực sự thiếu.
ProfK

1
Cảm ơn process.StartInfo.RedirectStandardError = true;if (process.ExitCode == 0)câu trả lời được chấp nhận không có.
JohnB

12

Câu trả lời được chấp nhận trên trang này có một điểm yếu là rắc rối trong các tình huống hiếm gặp. Có hai tệp xử lý mà các chương trình ghi theo quy ước, thiết bị xuất chuẩn và thiết bị xuất chuẩn. Nếu bạn chỉ đọc một tệp xử lý đơn lẻ như câu trả lời từ Ray và chương trình bạn đang bắt đầu ghi đủ đầu ra cho thiết bị lỗi chuẩn, nó sẽ lấp đầy bộ đệm và khối stderr đầu ra. Sau đó, hai quá trình của bạn đang bế tắc. Kích thước bộ đệm có thể là 4K. Điều này là cực kỳ hiếm trên các chương trình có thời gian ngắn, nhưng nếu bạn có một chương trình chạy dài liên tục xuất ra stderr, điều đó sẽ xảy ra cuối cùng. Đây là khó khăn để gỡ lỗi và theo dõi.

Có một vài cách tốt để đối phó với điều này.

  1. Một cách là thực thi cmd.exe thay vì chương trình của bạn và sử dụng đối số / c để cmd.exe để gọi chương trình của bạn cùng với đối số "2> & 1" tới cmd.exe để yêu cầu nó hợp nhất stdout và stderr.

            var p = new Process();
            p.StartInfo.FileName = "cmd.exe";
            p.StartInfo.Arguments = "/c mycmd.exe 2>&1";
  2. Một cách khác là sử dụng một mô hình lập trình đọc cả hai tay cầm cùng một lúc.

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

2
Tôi nghĩ rằng điều này trả lời câu hỏi ban đầu tốt hơn, vì nó cho thấy cách chạy lệnh CMD thông qua C # (không phải là một tệp).
TinyRacoon

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

Có thể bế tắc khi quá trình viết rất nhiều dữ liệu. Tốt hơn là bắt đầu đọc dữ liệu trong khi quá trình vẫn chạy.
JensG

6

Bạn sẽ cần sử dụng ProcessStartInfovới RedirectStandardOutputkích hoạt - sau đó bạn có thể đọc luồng đầu ra. Bạn có thể thấy dễ dàng hơn khi sử dụng ">" để chuyển hướng đầu ra sang tệp (thông qua HĐH), sau đó chỉ cần đọc tệp.

[chỉnh sửa: như những gì Ray đã làm: +1]


10
Điều đó buộc bạn phải viết một tệp ở đâu đó mà bạn cần sự cho phép, cần tìm một vị trí và tên và không được quên xóa khi bạn hoàn thành nó. Dễ dàng hơn để sử dụng RedirectStandardOutputthực sự.
peSHIr

4

Nếu bạn không ngại giới thiệu một phụ thuộc, CliWrap có thể đơn giản hóa việc này cho bạn:

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

3

Đây có thể không phải là cách tốt nhất / dễ nhất, nhưng có thể là một lựa chọn:

Khi bạn thực thi từ mã của mình, hãy thêm "> output.txt" và sau đó đọc trong tệp output.txt.


3

Bạn có thể khởi chạy bất kỳ chương trình dòng lệnh nào bằng lớp Process và đặt thuộc tính StandardOutput của đối tượng Process với trình đọc luồng bạn tạo (dựa trên chuỗi hoặc vị trí bộ nhớ). Sau khi quá trình hoàn tất, bạn có thể thực hiện bất kỳ khác biệt nào bạn cần trên luồng đó.


3

Điều này có thể hữu ích cho ai đó nếu bạn cố truy vấn bộ đệm ARP cục bộ trên PC / Server.

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

3

Lệnh chạy một lớp lót:

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

Đọc đầu ra của lệnh với số lượng mã khả thi ngắn nhất:

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


2

Trong trường hợp bạn cũng cần thực thi một số lệnh trong cmd.exe, bạn có thể làm như sau:

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

Điều này chỉ trả về đầu ra của chính lệnh:

nhập mô tả hình ảnh ở đây

Bạn cũng có thể sử dụng StandardInputthay vì 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);

Kết quả trông như thế này:

nhập mô tả hình ảnh ở đây


0

Để giải trí, đây là giải pháp hoàn chỉnh của tôi để nhận đầu ra PYTHON - dưới một nút bấm - với báo cáo lỗi. Chỉ cần thêm một nút gọi là "butPython" và nhãn gọi là "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); 
            }
    }

0

Vì hầu hết các câu trả lời ở đây không thực hiện sự trì usingtrệ IDisposablevà một số nội dung khác tôi nghĩ có thể không cần thiết nên tôi sẽ thêm câu trả lời này.

Dành cho 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");
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.