Cara: Jalankan baris perintah di C #, dapatkan hasil STD OUT

472

Bagaimana cara menjalankan program baris perintah dari C # dan mendapatkan kembali hasil STD OUT? Secara khusus, saya ingin menjalankan DIFF pada dua file yang dipilih secara terprogram dan menulis hasilnya ke kotak teks.

Sayap
sumber
2
Lihat juga stackoverflow.com/a/5367686/492 - ini menunjukkan acara untuk keluaran dan kesalahan.
CAD cowok
Terkait (tetapi tanpa menangkap STDOUT): stackoverflow.com/questions/1469764
user202729

Jawaban:

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

Kode berasal dari MSDN .

Ray Jezek
sumber
8
Apakah ada cara untuk melakukan ini tanpa file batch? Masalahnya, saya perlu mengirim beberapa parameter ke perintah. Saya menggunakan xsd.exe <Assembly> / type: <ClassName>, jadi saya harus dapat mengatur Assembly dan ClassName, dan kemudian jalankan perintah.
Carlo
26
Anda dapat menambahkan argumen ke panggilan Anda melalui {YourProcessObject}.StartInfo.Argumentsstring.
patridge
5
Bagaimana cara menjalankan proses sebagai administrator?
Saher Ahwal
5
Saya telah menemui sejumlah masalah di mana proses saya, menggunakan kode ini, berhenti sepenuhnya karena proses telah menulis data yang cukup ke p.StandardErroraliran. Ketika aliran menjadi penuh, tampaknya proses akan berhenti sampai data dikonsumsi, jadi saya harus membaca keduanya StandardErrordan StandardOutputuntuk memastikan bahwa tugas dijalankan dengan benar.
Ted Spence
5
Headup cepat dari c # compiler: Objek Process harus memiliki properti UseShellExecute disetel ke false untuk mengarahkan aliran IO.
IbrarMumtaz
144

Berikut ini contoh cepat:

//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();
Jeremy
sumber
2
+1 untuk menunjukkan cara menambahkan argumen ke menjalankan program baris perintah (yang tidak diterima jawaban yang diterima.)
Suman
104

Ada satu parameter lain yang saya temukan berguna, yang saya gunakan untuk menghilangkan jendela proses

pProcess.StartInfo.CreateNoWindow = true;

ini membantu untuk menyembunyikan jendela konsol hitam dari pengguna sepenuhnya, jika itu yang Anda inginkan.

Peter Du
sumber
3
Menyelamatkan saya banyak sakit kepala. Terima kasih.
The Vivandiere
2
Saat memanggil "sc", saya juga harus mengatur 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) +
        "'";
}
Lu55
sumber
3
Contoh yang sangat komprehensif, Terima kasih
ShahidAzim
2
Mungkin ingin mengubah handler OutputDataReceived menjadi stdOut.AppendLine ()
Paul Williams
3
Menurut pendapat saya, ini adalah solusi yang jauh lebih komprehensif daripada jawaban yang diterima. Saya menggunakannya sekarang, dan belum pernah menggunakan yang diterima, tapi yang itu terlihat kurang.
ProfK
1
Terima kasih process.StartInfo.RedirectStandardError = true;dan if (process.ExitCode == 0)jawaban yang diterima tidak ada.
JohnB
12

Jawaban yang diterima di halaman ini memiliki kelemahan yang merepotkan dalam situasi yang jarang terjadi. Ada dua file menangani program yang menulis dengan konvensi, stdout, dan stderr. Jika Anda baru saja membaca satu pegangan file seperti jawaban dari Ray, dan program yang Anda mulai tulis output yang cukup untuk stderr, itu akan mengisi buffer dan blok stderr output. Kemudian kedua proses Anda menemui jalan buntu. Ukuran buffer mungkin 4K. Ini sangat jarang terjadi pada program yang berumur pendek, tetapi jika Anda memiliki program yang berjalan lama yang berulang kali menghasilkan stderr, pada akhirnya akan terjadi. Ini sulit untuk di-debug dan dilacak.

Ada beberapa cara yang baik untuk mengatasi ini.

  1. Salah satu caranya adalah dengan mengeksekusi cmd.exe daripada program Anda dan menggunakan argumen / c untuk cmd.exe untuk memanggil program Anda bersama dengan argumen "2> & 1" ke cmd.exe untuk memerintahkannya menggabungkan stdout dan stderr.

            var p = new Process();
            p.StartInfo.FileName = "cmd.exe";
            p.StartInfo.Arguments = "/c mycmd.exe 2>&1";
  2. Cara lain adalah dengan menggunakan model pemrograman yang membaca kedua pegangan pada saat yang sama.

            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();
Cameron
sumber
2
Saya pikir ini menjawab pertanyaan asli lebih baik, karena ini menunjukkan bagaimana menjalankan perintah CMD melalui C # (bukan file).
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();
 }
Jeff Mc
sumber
Dapat menemui jalan buntu ketika proses menulis banyak data. Lebih baik mulai membaca data saat proses masih berjalan.
JensG
6

Anda akan perlu menggunakan ProcessStartInfodenganRedirectStandardOutput diaktifkan - maka Anda dapat membaca aliran output. Anda mungkin merasa lebih mudah menggunakan ">" untuk mengarahkan ulang output ke file (melalui OS), dan kemudian cukup membaca file.

[edit: seperti yang dilakukan Ray: +1]

Marc Gravell
sumber
10
Itu memaksa Anda untuk menulis file di suatu tempat yang Anda perlu izin, perlu menemukan lokasi dan nama untuk dan jangan lupa untuk menghapus ketika Anda selesai dengan itu. Lebih mudah digunakan RedirectStandardOutputsebenarnya.
RASHIr
4

Jika Anda tidak keberatan memperkenalkan dependensi, CliWrap dapat menyederhanakan ini untuk Anda:

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

Ini mungkin bukan cara terbaik / termudah, tetapi mungkin opsi:

Ketika Anda mengeksekusi dari kode Anda, tambahkan "> output.txt" dan kemudian baca di file output.txt.

Kon
sumber
3

Anda dapat meluncurkan program baris perintah apa pun menggunakan kelas Proses, dan mengatur properti StandardOutput dari instance Proses dengan pembaca aliran yang Anda buat (baik berdasarkan string atau lokasi memori). Setelah proses selesai, Anda dapat melakukan apa pun yang perlu dilakukan pada aliran itu.

Nick
sumber
3

Ini mungkin berguna bagi seseorang jika Anda mencoba untuk menanyakan cache ARP lokal pada 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();
        }
Kitson88
sumber
3

Perintah jalankan satu-liner:

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

Baca output dari perintah dalam jumlah terpendek dari kode yang dapat digunakan kembali:

    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();
Dan Stevens
sumber
2

Ada Kelas ProcessHelper di kode sumber terbuka PublicDomain yang mungkin menarik bagi Anda.

Shaik
sumber
2

Jika Anda juga perlu menjalankan beberapa perintah di cmd.exe, Anda dapat melakukan hal berikut:

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

Ini hanya mengembalikan output dari perintah itu sendiri:

masukkan deskripsi gambar di sini

Anda juga dapat menggunakan StandardInputalih-alih 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);

Hasilnya terlihat seperti ini:

masukkan deskripsi gambar di sini

Konard
sumber
0

Hanya untuk bersenang-senang, inilah solusi lengkap saya untuk mendapatkan output PYTHON - di bawah klik tombol - dengan pelaporan kesalahan. Cukup tambahkan tombol yang disebut "butPython" dan label yang disebut "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); 
            }
    }
Dave
sumber
0

Karena sebagian besar jawaban di sini tidak menerapkan usingstatemant untuk IDisposabledan beberapa hal lain yang saya pikir bisa jadi perlu saya akan menambahkan jawaban ini.

Untuk 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");
}
Julian
sumber