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.

Flügel
quelle
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 .

Ray Jezek
quelle
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();
Jeremy
quelle
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.

Peter Du
quelle
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) +
        "'";
}
Lu55
quelle
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();
Cameron
quelle
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();
 }
Jeff Mc
quelle
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]

Marc Gravell
quelle
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;
Tyrrrz
quelle
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.

Kon
quelle
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.

Nick
quelle
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();
        }
Kitson88
quelle
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();
Dan Stevens
quelle
2

In PublicDomain Open Source Code gibt es eine ProcessHelper-Klasse, die Sie interessieren könnte.

Shaik
quelle
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

Konard
quelle
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); 
            }
    }
Dave
quelle
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");
}
julianisch
quelle