วิธีการ: เรียกใช้งานบรรทัดคำสั่งใน C # รับผลลัพธ์ STD OUT


472

ฉันจะรันโปรแกรมบรรทัดคำสั่งจาก C # และกลับผลลัพธ์ STD OUT ได้อย่างไร โดยเฉพาะฉันต้องการรัน DIFF ในสองไฟล์ที่เลือกโดยทางโปรแกรมและเขียนผลลัพธ์ลงในกล่องข้อความ


2
ดูเพิ่มเติมที่stackoverflow.com/a/5367686/492 - แสดงเหตุการณ์สำหรับเอาต์พุตและข้อผิดพลาด
เจ้าหมอ CAD

ที่เกี่ยวข้อง (แต่ไม่มีการจับภาพ STDOUT): stackoverflow.com/questions/1469764
user202729

คำตอบ:


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

รหัสคือจากMSDN


8
มีวิธีการทำเช่นนี้โดยไม่มีไฟล์แบตช์หรือไม่? สิ่งที่ฉันต้องส่งพารามิเตอร์บางอย่างไปยังคำสั่ง ฉันใช้ xsd.exe <Assembly> / type: <ClassName> ดังนั้นฉันต้องสามารถตั้งค่าทั้ง Assembly และ ClassName แล้วเรียกใช้คำสั่ง
Carlo

26
คุณสามารถเพิ่มข้อโต้แย้งในการโทรของคุณผ่าน{YourProcessObject}.StartInfo.Argumentsสายอักขระ
patridge

5
จะทำให้กระบวนการทำงานในฐานะผู้ดูแลระบบได้อย่างไร?
Saher Ahwal

5
ฉันพบปัญหาหลายอย่างที่กระบวนการของฉันโดยใช้รหัสนี้หยุดอย่างสมบูรณ์เนื่องจากกระบวนการเขียนข้อมูลที่เพียงพอไปยังp.StandardErrorสตรีม เมื่อกระแสเต็มจะปรากฏว่ากระบวนการจะหยุดจนกว่าข้อมูลจะถูกใช้ดังนั้นฉันต้องอ่านทั้งสองStandardErrorและStandardOutputเพื่อให้มั่นใจว่างานดำเนินการอย่างถูกต้อง
Ted Spence

5
Headsup ด่วนจากคอมไพเลอร์ c #: กระบวนการวัตถุต้องมีคุณสมบัติ UseShellExecute ตั้งค่าเป็นเท็จเพื่อเปลี่ยนเส้นทางสตรีม IO
IbrarMumtaz

144

นี่คือตัวอย่างรวดเร็ว:

//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 สำหรับการแสดงวิธีการเพิ่มอาร์กิวเมนต์เพื่อเรียกใช้โปรแกรมบรรทัดคำสั่ง (ซึ่งคำตอบที่ยอมรับไม่ได้มี)
Suman

104

มีอีกพารามิเตอร์หนึ่งที่ฉันพบว่ามีประโยชน์ซึ่งฉันใช้เพื่อกำจัดหน้าต่างกระบวนการ

pProcess.StartInfo.CreateNoWindow = true;

สิ่งนี้ช่วยในการซ่อนหน้าต่างคอนโซลสีดำจากผู้ใช้อย่างสมบูรณ์ถ้านั่นคือสิ่งที่คุณต้องการ


3
ช่วยให้ฉันปวดหัวมาก ขอบคุณ
Vivandiere

2
เมื่อเรียก "sc" ฉันต้องตั้ง 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
ตัวอย่างที่ครอบคลุมมากขอบคุณ
ShahidAzim

2
อาจต้องการเปลี่ยนเครื่องมือจัดการ OutputDataReceived เป็น stdOut.AppendLine ()
Paul Williams

3
ในความคิดของฉันนี้เป็นโซลูชั่นที่ครอบคลุมมากกว่าคำตอบที่ยอมรับ ตอนนี้ฉันกำลังใช้อยู่และไม่ได้ใช้ที่ยอมรับ แต่อันที่จริงแล้วมันขาดไป
ProfK

1
ขอขอบคุณprocess.StartInfo.RedirectStandardError = true;และif (process.ExitCode == 0)คำตอบที่ยอมรับไม่มี
JohnB

12

คำตอบที่ยอมรับในหน้านี้มีจุดอ่อนที่ลำบากในสถานการณ์ที่หายาก มีสองจัดการไฟล์ที่โปรแกรมเขียนโดยการประชุม, stdout และ stderr หากคุณเพิ่งอ่านตัวจัดการไฟล์เดียวเช่นคำตอบจาก Ray และโปรแกรมที่คุณเริ่มเขียนเอาต์พุตเพียงพอไปยัง stderr มันจะเติมบัฟเฟอร์ stderr และบล็อกเอาท์พุทให้เต็ม จากนั้นกระบวนการทั้งสองของคุณจะหยุดชะงัก ขนาดบัฟเฟอร์อาจเป็น 4K นี่เป็นสิ่งที่หายากมากในโปรแกรมระยะสั้น แต่ถ้าคุณมีโปรแกรมที่ใช้เวลานานซึ่งส่งไปยัง stderr ซ้ำ ๆ มันจะเกิดขึ้นในที่สุด นี่เป็นเรื่องยากที่จะตรวจแก้จุดบกพร่องและติดตาม

มีวิธีที่ดีสองสามข้อในการจัดการกับสิ่งนี้

  1. วิธีหนึ่งคือการเรียกใช้ cmd.exe แทนโปรแกรมของคุณและใช้อาร์กิวเมนต์ / c เพื่อ cmd.exe เพื่อเรียกใช้โปรแกรมของคุณพร้อมกับอาร์กิวเมนต์ "2> & 1" ไปยัง cmd.exe เพื่อบอกให้ผสาน 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();

2
ฉันคิดว่านี่เป็นคำตอบที่ดีกว่าเดิมเพราะมันแสดงวิธีเรียกใช้คำสั่ง CMD ผ่าน C # (ไม่ใช่ไฟล์)
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();
 }

อาจหยุดชะงักเมื่อกระบวนการเขียนข้อมูลจำนวนมาก เป็นการดีกว่าที่จะเริ่มอ่านข้อมูลในขณะที่กระบวนการยังทำงานอยู่
JensG

6

คุณจะต้องใช้ProcessStartInfoกับRedirectStandardOutputเปิดใช้งาน - จากนั้นคุณสามารถอ่านเอาต์พุตสตรีมได้ คุณอาจพบว่าใช้ ">" เพื่อเปลี่ยนเส้นทางเอาต์พุตไปยังไฟล์ (ผ่านระบบปฏิบัติการ) ได้ง่ายขึ้นจากนั้นอ่านไฟล์

[แก้ไข: เหมือนที่เรย์ทำ: +1]


10
ที่บังคับให้คุณเขียนไฟล์บางแห่งที่คุณต้องการการอนุญาตต้องค้นหาสถานที่และชื่อและต้องไม่ลืมที่จะลบเมื่อคุณทำเสร็จแล้ว ใช้งานง่ายขึ้นRedirectStandardOutputจริง
peSHIr

4

หากคุณไม่สนใจที่จะแนะนำการพึ่งพาCliWrapสามารถทำให้สิ่งนี้ง่ายขึ้นสำหรับคุณ:

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

3

นี่อาจไม่ใช่วิธีที่ดีที่สุด / ง่ายที่สุด แต่อาจเป็นตัวเลือก:

เมื่อคุณดำเนินการจากรหัสของคุณเพิ่ม "> output.txt" แล้วอ่านในไฟล์ output.txt


3

คุณสามารถเรียกใช้โปรแกรมบรรทัดคำสั่งใดก็ได้โดยใช้คลาส Process และตั้งค่าคุณสมบัติ StandardOutput ของอินสแตนซ์กระบวนการด้วยเครื่องอ่านสตรีมที่คุณสร้าง (ขึ้นอยู่กับสตริงหรือตำแหน่งหน่วยความจำ) หลังจากกระบวนการเสร็จสมบูรณ์คุณสามารถทำสิ่งที่แตกต่างที่คุณต้องการในสตรีมนั้น


3

สิ่งนี้อาจมีประโยชน์สำหรับบางคนหากคุณพยายามที่จะค้นหาแคช 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();
        }

3

คำสั่งเรียกใช้หนึ่งซับ:

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


2

ในกรณีที่คุณต้องดำเนินการคำสั่งบางอย่างใน 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);

ผลลัพธ์จะเป็นดังนี้:

ป้อนคำอธิบายรูปภาพที่นี่


0

เพียงเพื่อความสนุกนี่คือโซลูชันที่สมบูรณ์ของฉันสำหรับการรับเอาต์พุต 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); 
            }
    }

0

เนื่องจากคำตอบส่วนใหญ่ที่นี่ไม่ใช้usingstatemant สำหรับIDisposableและสิ่งอื่น ๆ ที่ฉันคิดว่าอาจเป็นสิ่งจำเป็นฉันจะเพิ่มคำตอบนี้

สำหรับ 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");
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.