Cómo: Ejecutar la línea de comando en C #, obtener resultados STD OUT

472

¿Cómo ejecuto un programa de línea de comandos desde C # y obtengo los resultados STD OUT? Específicamente, quiero ejecutar DIFF en dos archivos que se seleccionan mediante programación y escribir los resultados en un cuadro de texto.

Ala
fuente
2
Consulte también stackoverflow.com/a/5367686/492 : muestra eventos de salida y errores.
CAD bloke
Relacionado (pero sin capturar STDOUT): stackoverflow.com/questions/1469764
user202729

Respuestas:

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

El código es de MSDN .

Ray Jezek
fuente
8
¿Hay alguna manera de hacer esto sin un archivo por lotes? La cosa es que necesito enviar algunos parámetros al comando. Estoy usando xsd.exe <Assembly> / type: <ClassName>, por lo que necesito poder configurar tanto el ensamblado como el ClassName y luego ejecutar el comando.
Carlo
26
Puede agregar argumentos a su llamada a través de la {YourProcessObject}.StartInfo.Argumentscadena.
patridge
55
¿Cómo hacer que el proceso se ejecute como administrador?
Saher Ahwal
55
He encontrado una serie de problemas en los que mi proceso, usando este código, se detiene por completo porque el proceso ha escrito suficientes datos en la p.StandardErrorsecuencia. Cuando la secuencia se llena, parece que el proceso se detendrá hasta que se consuman los datos, por lo que tengo que leer ambos StandardErrory StandardOutputpara garantizar que una tarea se ejecute correctamente.
Ted Spence
55
Aviso rápido del compilador de C #: el objeto Process debe tener la propiedad UseShellExecute establecida en false para redirigir las secuencias de E / S.
IbrarMumtaz
144

Aquí hay una muestra rápida:

//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
fuente
2
+1 para mostrar cómo agregar argumentos para ejecutar un programa de línea de comandos (que la respuesta aceptada no tiene)
Suman
104

Hay otro parámetro que encontré útil, que utilizo para eliminar la ventana del proceso

pProcess.StartInfo.CreateNoWindow = true;

esto ayuda a ocultar la ventana de la consola negra del usuario por completo, si eso es lo que desea.

Peter Du
fuente
3
Me ahorró mucho dolor de cabeza. Gracias.
The Vivandiere
2
Al llamar a "sc", también tuve que configurar 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
fuente
3
Un ejemplo muy completo, gracias
ShahidAzim
2
Es posible que desee cambiar el controlador OutputDataReceived a stdOut.AppendLine ()
Paul Williams
3
En mi opinión, esta es una solución mucho más integral que la respuesta aceptada. Lo estoy usando ahora, y no he usado el aceptado, pero realmente parece que falta.
ProfK
1
Gracias por process.StartInfo.RedirectStandardError = true;y if (process.ExitCode == 0)qué respuesta aceptada no tiene.
JohnB
12

La respuesta aceptada en esta página tiene una debilidad que es problemática en situaciones raras. Hay dos identificadores de archivo en los que los programas escriben por convención, stdout y stderr. Si acaba de leer un identificador de archivo único, como la respuesta de Ray, y el programa que está iniciando escribe suficiente salida para stderr, llenará el búfer y bloque stderr de salida. Entonces sus dos procesos están estancados. El tamaño del búfer puede ser 4K. Esto es extremadamente raro en los programas de corta duración, pero si tiene un programa de larga ejecución que genera repetidamente salidas en stderr, sucederá eventualmente. Esto es difícil de depurar y rastrear.

Hay un par de buenas maneras de lidiar con esto.

  1. Una forma es ejecutar cmd.exe en lugar de su programa y usar el argumento / c para cmd.exe para invocar su programa junto con el argumento "2> & 1" a cmd.exe para indicarle que combine stdout y stderr.

            var p = new Process();
            p.StartInfo.FileName = "cmd.exe";
            p.StartInfo.Arguments = "/c mycmd.exe 2>&1";
  2. Otra forma es usar un modelo de programación que lea ambas manijas al mismo tiempo.

            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
fuente
2
Creo que esto responde mejor a la pregunta original, ya que muestra cómo ejecutar un comando CMD a través de C # (no un archivo).
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
fuente
Puede un punto muerto cuando el proceso escribe muchos datos. Es mejor comenzar a leer datos mientras el proceso aún se ejecuta.
JensG
6

Necesitará usar ProcessStartInfocon RedirectStandardOutputhabilitado; luego puede leer la secuencia de salida. Puede que le resulte más fácil usar ">" para redirigir la salida a un archivo (a través del sistema operativo) y luego simplemente leer el archivo.

[editar: como lo que hizo Ray: +1]

Marc Gravell
fuente
10
Eso lo obliga a escribir un archivo en algún lugar para el que necesita permiso, necesita encontrar una ubicación y un nombre y no debe olvidarse de eliminarlo cuando haya terminado con él. Más fácil de usar en RedirectStandardOutputrealidad.
PEHER el
4

Si no le importa introducir una dependencia, CliWrap puede simplificar esto para usted:

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

Puede que esta no sea la mejor / más fácil forma, pero puede ser una opción:

Cuando ejecute desde su código, agregue "> output.txt" y luego lea en el archivo output.txt.

Kon
fuente
3

Puede iniciar cualquier programa de línea de comandos utilizando la clase Process y establecer la propiedad StandardOutput de la instancia de Process con un lector de flujo que cree (ya sea basado en una cadena o en una ubicación de memoria). Una vez que se completa el proceso, puede hacer cualquier diferencia que necesite en esa secuencia.

Mella
fuente
3

Esto podría ser útil para alguien si intenta consultar el caché ARP local en una PC / Servidor.

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
fuente
3

Comando de ejecución de una línea:

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

Lea la salida del comando en la menor cantidad de código reable:

    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
fuente
2

Hay una clase ProcessHelper en el código fuente de PublicDomain que puede interesarle.

Shaik
fuente
2

En caso de que también necesite ejecutar algún comando en cmd.exe, puede hacer lo siguiente:

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

Esto devuelve solo la salida del comando en sí:

ingrese la descripción de la imagen aquí

También puede usar en StandardInputlugar de 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);

El resultado se ve así:

ingrese la descripción de la imagen aquí

Konard
fuente
0

Solo por diversión, aquí está mi solución completa para obtener la salida PYTHON, con un clic de botón, con informes de errores. Simplemente agregue un botón llamado "butPython" y una etiqueta llamada "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
fuente
0

Dado que la mayoría de las respuestas aquí no implementan el usingstatemant para IDisposabley otras cosas que creo que podrían ser nessecary, agregaré esta respuesta.

Para 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
fuente