Come: eseguire la riga di comando in C #, ottenere risultati STD OUT


472

Come eseguire un programma da riga di comando da C # e recuperare i risultati STD OUT? In particolare, voglio eseguire DIFF su due file che sono selezionati a livello di programmazione e scrivere i risultati in una casella di testo.


2
Vedi anche stackoverflow.com/a/5367686/492 - mostra eventi per output ed errori.
Bloke CAD,

Correlati (ma senza la cattura STDOUT): stackoverflow.com/questions/1469764
user202729

Risposte:


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

Il codice proviene da MSDN .


8
C'è un modo per farlo senza un file batch? Il fatto è che devo inviare alcuni parametri al comando. Sto usando xsd.exe <Assembly> / type: <ClassName>, quindi devo essere in grado di impostare sia Assembly che ClassName, quindi eseguire il comando.
Carlo,

26
È possibile aggiungere argomenti alla chiamata tramite la {YourProcessObject}.StartInfo.Argumentsstringa.
Patridge

5
Come eseguire il processo come amministratore?
Saher Ahwal,

5
Ho riscontrato una serie di problemi in cui il mio processo, usando questo codice, si interrompe completamente perché il processo ha scritto dati sufficienti nello p.StandardErrorstream. Quando lo stream si riempie, sembra che il processo si fermerà fino a quando i dati non saranno consumati, quindi devo leggere entrambi StandardErrore StandardOutputper garantire che un'attività venga eseguita correttamente.
Ted Spence,

5
Rapido headsup dal compilatore c #: l'oggetto Process deve avere la proprietà UseShellExecute impostata su false per reindirizzare i flussi IO.
IbrarMumtaz,

144

Ecco un breve esempio:

//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 per mostrare come aggiungere argomenti all'esecuzione di un programma da riga di comando (che la risposta accettata non ha.)
Suman

104

C'è un altro parametro che ho trovato utile, che uso per eliminare la finestra del processo

pProcess.StartInfo.CreateNoWindow = true;

questo aiuta a nascondere completamente la finestra della console nera all'utente, se è quello che desideri.


3
Mi ha risparmiato un sacco di mal di testa. Grazie.
Il Vivandiere,

2
Quando ho chiamato "sc" ho dovuto anche impostare 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
Un esempio molto completo, grazie
ShahidAzim,

2
Potrebbe voler cambiare il gestore OutputDataReceived a stdOut.AppendLine ()
Paul Williams,

3
A mio avviso, questa è una soluzione molto più completa della risposta accettata. Lo sto usando ora e non ho usato quello accettato, ma quello sembra davvero carente.
ProfK

1
Grazie per process.StartInfo.RedirectStandardError = true;e if (process.ExitCode == 0)quale risposta accettata non ha.
John B

12

La risposta accettata in questa pagina ha un punto debole che è problematico in rare situazioni. Esistono due handle di file su cui i programmi scrivono per convenzione, stdout e stderr. Se hai appena letto un handle di file singolo come la risposta di Ray, e il programma che stai iniziando scrive abbastanza output su stderr, riempirà il buffer e il blocco stderr di output. Quindi i tuoi due processi vengono bloccati. La dimensione del buffer può essere 4K. Ciò è estremamente raro sui programmi di breve durata, ma se si dispone di un programma a esecuzione prolungata che viene ripetutamente emesso su stderr, alla fine accadrà. È difficile eseguire il debug e rintracciare.

Ci sono un paio di buoni modi per affrontarlo.

  1. Un modo è quello di eseguire cmd.exe invece del programma e utilizzare l'argomento / c su cmd.exe per richiamare il programma insieme all'argomento "2> & 1" su cmd.exe per dirgli di unire stdout e stderr.

            var p = new Process();
            p.StartInfo.FileName = "cmd.exe";
            p.StartInfo.Arguments = "/c mycmd.exe 2>&1";
    
  2. Un altro modo è quello di utilizzare un modello di programmazione che legge entrambi gli handle contemporaneamente.

            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
Penso che questo risponda meglio alla domanda originale, in quanto mostra come eseguire un comando CMD tramite C # (non un 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();
 }

Può un deadlock quando il processo scrive molti dati. È meglio iniziare a leggere i dati mentre il processo è ancora in esecuzione.
JensG,

6

Dovrai utilizzare ProcessStartInfocon RedirectStandardOutputabilitato, quindi puoi leggere il flusso di output. Potrebbe essere più semplice utilizzare ">" per reindirizzare l'output su un file (tramite il sistema operativo), quindi leggere semplicemente il file.

[modifica: come quello che ha fatto Ray: +1]


10
Ciò ti obbliga a scrivere un file in un posto per il quale hai bisogno dell'autorizzazione, devi trovare una posizione e un nome e non dimenticare di eliminarlo quando hai finito. Più facile da usare in RedirectStandardOutputrealtà.
peSHIr

4

Se non ti dispiace introdurre una dipendenza, CliWrap può semplificarlo per te:

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

3

Questo potrebbe non essere il modo migliore / più semplice, ma potrebbe essere un'opzione:

Quando esegui dal tuo codice, aggiungi "> output.txt" e leggi nel file output.txt.


3

È possibile avviare qualsiasi programma da riga di comando utilizzando la classe Process e impostare la proprietà StandardOutput dell'istanza Process con un lettore di flussi creato (basato su una stringa o una posizione di memoria). Una volta completato il processo, puoi fare qualsiasi diff di cui hai bisogno su quel flusso.


3

Questo potrebbe essere utile per qualcuno se stai tentando di interrogare la cache ARP locale su un 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

Comando di esecuzione di una riga:

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

Leggi l'output del comando nella quantità più breve di codice raggiungibile:

    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

Nel caso in cui sia necessario eseguire anche alcuni comandi nel cmd.exe, è possibile effettuare le seguenti operazioni:

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

Questo restituisce solo l'output del comando stesso:

inserisci qui la descrizione dell'immagine

Puoi anche usare StandardInputinvece di 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);

Il risultato è simile al seguente:

inserisci qui la descrizione dell'immagine


0

Solo per divertimento, ecco la mia soluzione completa per ottenere l'output di PYTHON - con un clic del pulsante - con la segnalazione degli errori. Basta aggiungere un pulsante chiamato "butPython" e un'etichetta chiamata "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

Dal momento che la maggior parte delle risposte qui non implementano la usingmotivazione dello stato IDisposablee alcune altre cose che penso possano essere necessarie, aggiungerò questa risposta.

Per 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");
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.