Gewusst wie: Führen Sie die Befehlszeile in C # aus und erhalten Sie die STD OUT-Ergebnisse


472

Wie führe ich ein Befehlszeilenprogramm von C # aus und erhalte die STD OUT-Ergebnisse zurück? Insbesondere möchte ich DIFF für zwei Dateien ausführen, die programmgesteuert ausgewählt wurden, und die Ergebnisse in ein Textfeld schreiben.


2
Siehe auch stackoverflow.com/a/5367686/492 - es werden Ereignisse für die Ausgabe und Fehler angezeigt .
CAD Kerl

Verwandte (aber ohne STDOUT zu erfassen): stackoverflow.com/questions/1469764
user202729

Antworten:


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

Der Code stammt von MSDN .


8
Gibt es eine Möglichkeit, dies ohne Batch-Datei zu tun? Ich muss einige Parameter an den Befehl senden. Ich verwende die Datei xsd.exe <Assembly> / type: <ClassName>, daher muss ich in der Lage sein, sowohl die Assembly als auch den ClassName festzulegen und dann den Befehl auszuführen.
Carlo

26
Sie können Ihrem Aufruf über die {YourProcessObject}.StartInfo.ArgumentsZeichenfolge Argumente hinzufügen .
Patridge

5
Wie kann der Prozess als Administrator ausgeführt werden?
Saher Ahwal

5
Ich bin auf eine Reihe von Problemen gestoßen, bei denen mein Prozess, der diesen Code verwendet, vollständig angehalten wird, weil der Prozess genügend Daten in den p.StandardErrorStream geschrieben hat. Wenn der Stream voll ist, scheint der Prozess anzuhalten, bis die Daten verbraucht sind. Daher muss ich beide lesen StandardErrorund StandardOutputsicherstellen, dass eine Aufgabe korrekt ausgeführt wird.
Ted Spence

5
Schnelles Headsup vom c # -Compiler: Für das Process-Objekt muss die UseShellExecute-Eigenschaft auf false festgelegt sein, damit E / A-Streams umgeleitet werden können.
IbrarMumtaz

144

Hier ist ein kurzes Beispiel:

//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 für das Zeigen, wie man Argumente zum Ausführen eines Befehlszeilenprogramms hinzufügt (was die akzeptierte Antwort nicht hat.)
Suman

104

Dort fand ich einen weiteren Parameter nützlich, mit dem ich das Prozessfenster eliminiere

pProcess.StartInfo.CreateNoWindow = true;

Dies hilft, das schwarze Konsolenfenster vollständig vor dem Benutzer zu verbergen, wenn Sie dies wünschen.


3
Hat mir viel Kopfschmerzen erspart. Vielen Dank.
Die Vivandiere

2
Beim Aufruf von "sc" musste ich auch StartInfo.WindowStyle = ProcessWindowStyle.Hidden setzen.
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
Ein sehr umfassendes Beispiel, Danke
ShahidAzim

2
Vielleicht möchten Sie den OutputDataReceived-Handler in stdOut.AppendLine () ändern
Paul Williams

3
Meiner Meinung nach ist dies eine viel umfassendere Lösung als die akzeptierte Antwort. Ich benutze es jetzt und habe das akzeptierte nicht benutzt, aber das sieht wirklich mangelhaft aus.
ProfK

1
Danke für process.StartInfo.RedirectStandardError = true;und if (process.ExitCode == 0)welche akzeptierte Antwort hat nicht.
JohnB

12

Die akzeptierte Antwort auf dieser Seite weist eine Schwäche auf, die in seltenen Situationen problematisch ist. Es gibt zwei Dateihandles, in die Programme gemäß Konvention schreiben: stdout und stderr. Wenn Sie nur ein einzelnes Dateihandle wie die Antwort von Ray lesen und das Programm, das Sie starten, genügend Ausgabe in stderr schreibt, füllt es den Ausgabe-Stderr-Puffer und -Block. Dann sind Ihre beiden Prozesse festgefahren. Die Puffergröße kann 4 KB betragen. Dies ist bei kurzlebigen Programmen äußerst selten, aber wenn Sie ein lang laufendes Programm haben, das wiederholt an stderr ausgegeben wird, wird es irgendwann passieren. Dies ist schwierig zu debuggen und aufzuspüren.

Es gibt ein paar gute Möglichkeiten, damit umzugehen.

  1. Eine Möglichkeit besteht darin, cmd.exe anstelle Ihres Programms auszuführen und das Argument / c für cmd.exe zu verwenden, um Ihr Programm zusammen mit dem Argument "2> & 1" für cmd.exe aufzurufen, damit stdout und stderr zusammengeführt werden.

            var p = new Process();
            p.StartInfo.FileName = "cmd.exe";
            p.StartInfo.Arguments = "/c mycmd.exe 2>&1";
  2. Eine andere Möglichkeit besteht darin, ein Programmiermodell zu verwenden, das beide Handles gleichzeitig liest.

            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
Ich denke, dies beantwortet die ursprüngliche Frage besser, da es zeigt, wie ein CMD-Befehl über C # (keine Datei) ausgeführt wird.
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();
 }

Kann zum Stillstand kommen, wenn der Prozess viele Daten schreibt. Es ist besser, mit dem Lesen von Daten zu beginnen, während der Prozess noch ausgeführt wird.
JensG

6

Sie müssen ProcessStartInfomit RedirectStandardOutputaktiviert verwenden - dann können Sie den Ausgabestream lesen. Möglicherweise ist es einfacher, ">" zu verwenden, um die Ausgabe in eine Datei umzuleiten (über das Betriebssystem) und dann einfach die Datei zu lesen.

[bearbeiten: wie Ray es getan hat: +1]


10
Das zwingt Sie dazu, eine Datei an eine Stelle zu schreiben, für die Sie eine Berechtigung benötigen, einen Speicherort und einen Namen finden müssen, und darf nicht vergessen, sie zu löschen, wenn Sie damit fertig sind. Einfacher zu bedienen RedirectStandardOutput.
PeSHIr

4

Wenn es Ihnen nichts ausmacht, eine Abhängigkeit einzuführen, kann CliWrap dies für Sie vereinfachen:

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

3

Dies ist möglicherweise nicht der beste / einfachste Weg, kann aber eine Option sein:

Wenn Sie aus Ihrem Code ausführen, fügen Sie "> output.txt" hinzu und lesen Sie dann die Datei output.txt ein.


3

Sie können jedes Befehlszeilenprogramm mit der Process-Klasse starten und die StandardOutput-Eigenschaft der Process-Instanz mit einem von Ihnen erstellten Stream-Reader festlegen (entweder basierend auf einer Zeichenfolge oder einem Speicherort). Nachdem der Vorgang abgeschlossen ist, können Sie alle erforderlichen Änderungen an diesem Stream vornehmen.


3

Dies kann für jemanden nützlich sein, wenn Sie versuchen, den lokalen ARP-Cache auf einem PC / Server abzufragen.

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

Einzeiliger Ausführungsbefehl:

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

Lesen Sie die Ausgabe des Befehls in kürzester Menge des Reable-Codes:

    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

Falls Sie auch einen Befehl in der cmd.exe ausführen müssen, können Sie Folgendes tun:

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

Dies gibt nur die Ausgabe des Befehls selbst zurück:

Geben Sie hier die Bildbeschreibung ein

Sie können auch StandardInputanstelle von 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);

Das Ergebnis sieht folgendermaßen aus:

Geben Sie hier die Bildbeschreibung ein


0

Nur zum Spaß, hier ist meine fertige Lösung, um PYTHON-Ausgabe - mit einem Klick auf eine Schaltfläche - mit Fehlerberichterstattung zu erhalten. Fügen Sie einfach eine Schaltfläche namens "butPython" und eine Bezeichnung namens "llHello" hinzu ...

    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

Da die meisten Antworten hier implementieren nicht den usingstatemant für IDisposableund einige andere Sachen Wich ich denken konnte nessecary sein ich diese Antwort hinzufügen.

Für 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");
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.