Capturando el resultado de la consola desde una aplicación .NET (C #)

130

¿Cómo invoco una aplicación de consola desde mi aplicación .NET y capturo toda la salida generada en la consola?

(Recuerde, no quiero guardar la información primero en un archivo y luego volver a enumerarla, ya que me encantaría recibirla en vivo).

Gripsoft
fuente
Posible duplicado de Process.start: ¿cómo obtener la salida?
Michael Freidgeim
Vea las fechas en ambas preguntas y vea cuál está "duplicado"
Gripsoft
"Posible duplicado" es una forma de limpieza: cerrar preguntas similares y mantener una con las mejores respuestas. La fecha no es esencial. Ver ¿Debo votar para cerrar una pregunta duplicada, aunque sea mucho más nueva y tenga respuestas más actualizadas? Si acepta que requiere aclaración, vote en Agregar enlace de aclaración al comentario automatizado "Posible duplicado"
Michael Freidgeim

Respuestas:

163

Esto se puede lograr fácilmente usando la propiedad ProcessStartInfo.RedirectStandardOutput . Una muestra completa está contenida en la documentación de MSDN vinculada; La única advertencia es que es posible que también deba redirigir la secuencia de error estándar para ver todos los resultados de su aplicación.

Process compiler = new Process();
compiler.StartInfo.FileName = "csc.exe";
compiler.StartInfo.Arguments = "/r:System.dll /out:sample.exe stdstr.cs";
compiler.StartInfo.UseShellExecute = false;
compiler.StartInfo.RedirectStandardOutput = true;
compiler.Start();    

Console.WriteLine(compiler.StandardOutput.ReadToEnd());

compiler.WaitForExit();
mdb
fuente
3
Si no quieres la nueva línea extra al final, solo Console.Writeúsala.
tm1
2
Cabe señalar que si usa ReadToEnd () en combinación con una aplicación de consola que tiene la capacidad de solicitar al usuario que ingrese. Por ejemplo: Sobrescribir archivo: ¿S o N? etc. Entonces, ReadToEnd puede provocar una pérdida de memoria, ya que el proceso nunca se cierra mientras se espera la entrada del usuario. La forma más segura de capturar la salida es utilizar el controlador de eventos process.OutputDataReceived y dejar que el proceso notifique a su aplicación la salida que se recibirá.
Baaleos
Cómo capturar si en caso de que el código se implemente en una aplicación web azul, ya que compiler.StartInfo.FileName = "csc.exe"; ¡podría no existir!
Asif Iqbal
Cómo capturar si en caso de que el código se implemente en una aplicación web azul, ya que compiler.StartInfo.FileName = "csc.exe"; ¡podría no existir!
Asif Iqbal
37

Esta es una pequeña mejora sobre la respuesta aceptada de @mdb . Específicamente, también capturamos la salida de error del proceso. Además, capturamos estas salidas a través de eventos, ya ReadToEnd()no funciona si desea capturar tanto error y salida normal. Me tomó un tiempo hacer que esto funcione porque en realidad también requiere BeginxxxReadLine()llamadas después Start().

Manera asincrónica:

using System.Diagnostics;

Process process = new Process();

void LaunchProcess()
{
    process.EnableRaisingEvents = true;
    process.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(process_OutputDataReceived);
    process.ErrorDataReceived += new System.Diagnostics.DataReceivedEventHandler(process_ErrorDataReceived);
    process.Exited += new System.EventHandler(process_Exited);

    process.StartInfo.FileName = "some.exe";
    process.StartInfo.Arguments = "param1 param2";
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardError = true;
    process.StartInfo.RedirectStandardOutput = true;

    process.Start();
    process.BeginErrorReadLine();
    process.BeginOutputReadLine();          

    //below line is optional if we want a blocking call
    //process.WaitForExit();
}

void process_Exited(object sender, EventArgs e)
{
    Console.WriteLine(string.Format("process exited with code {0}\n", process.ExitCode.ToString()));
}

void process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
    Console.WriteLine(e.Data + "\n");
}

void process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
    Console.WriteLine(e.Data + "\n");
}
Shital Shah
fuente
55
¡Gracias, he estado buscando esto por años!
C Bauer
3
Gracias. Esto es perfecto.
DrFloyd5
1
Obtendrá un lugar de honor en la lista de agradecimientos de mi solicitud.
marsh-wiggle
7

ConsoleAppLauncher es una biblioteca de código abierto creada específicamente para responder esa pregunta. Captura todos los resultados generados en la consola y proporciona una interfaz simple para iniciar y cerrar la aplicación de la consola.

El evento ConsoleOutput se activa cada vez que la consola escribe una nueva línea en la salida estándar / de error. Las líneas se ponen en cola y se garantiza que sigan el orden de salida.

También disponible como paquete NuGet .

Llamada de muestra para obtener la salida completa de la consola:

// Run simplest shell command and return its output.
public static string GetWindowsVersion()
{
    return ConsoleApp.Run("cmd", "/c ver").Output.Trim();
}

Muestra con comentarios en vivo:

// Run ping.exe asynchronously and return roundtrip times back to the caller in a callback
public static void PingUrl(string url, Action<string> replyHandler)
{
    var regex = new Regex("(time=|Average = )(?<time>.*?ms)", RegexOptions.Compiled);
    var app = new ConsoleApp("ping", url);
    app.ConsoleOutput += (o, args) =>
    {
        var match = regex.Match(args.Line);
        if (match.Success)
        {
            var roundtripTime = match.Groups["time"].Value;
            replyHandler(roundtripTime);
        }
    };
    app.Run();
}
SlavaGu
fuente
2

Agregué varios métodos auxiliares a la Plataforma O2 (proyecto de código abierto) que le permiten crear fácilmente una interacción con otro proceso a través de la salida y entrada de la consola (consulte http://code.google.com/p/o2platform/ source / browse / trunk / O2_Scripts / APIs / Windows / CmdExe / CmdExeAPI.cs )

También puede ser útil para usted la API que permite ver el resultado de la consola del proceso actual (en un control existente o ventana emergente). Consulte esta publicación de blog para obtener más detalles: http://o2platform.wordpress.com/2011/11/26/api_consoleout-cs-inprocess-capture-of-the-console-output/ (este blog también contiene detalles sobre cómo consumir la salida de la consola de nuevos procesos)

Dinis Cruz
fuente
Desde entonces, he agregado más soporte para usar ConsoleOut (en este caso, si inicia el proceso .NET usted mismo). Eche un vistazo a: Cómo usar la salida de la Consola en C # REPL , Agregar 'Console Out' a VisualStudio IDE como una ventana nativa , Ver mensajes de 'Console Out' creados dentro de UserControls
Dinis Cruz
2

Hice una versión reactiva que acepta devoluciones de llamada para stdOut y StdErr.
onStdOuty onStdErrse llaman de forma asíncrona,
tan pronto como llegan los datos (antes de que salga el proceso).

public static Int32 RunProcess(String path,
                               String args,
                       Action<String> onStdOut = null,
                       Action<String> onStdErr = null)
    {
        var readStdOut = onStdOut != null;
        var readStdErr = onStdErr != null;

        var process = new Process
        {
            StartInfo =
            {
                FileName = path,
                Arguments = args,
                CreateNoWindow = true,
                UseShellExecute = false,
                RedirectStandardOutput = readStdOut,
                RedirectStandardError = readStdErr,
            }
        };

        process.Start();

        if (readStdOut) Task.Run(() => ReadStream(process.StandardOutput, onStdOut));
        if (readStdErr) Task.Run(() => ReadStream(process.StandardError, onStdErr));

        process.WaitForExit();

        return process.ExitCode;
    }

    private static void ReadStream(TextReader textReader, Action<String> callback)
    {
        while (true)
        {
            var line = textReader.ReadLine();
            if (line == null)
                break;

            callback(line);
        }
    }


Ejemplo de uso

A continuación se ejecutará executablecon argse imprimir

  • stdOut en blanco
  • stdErr en rojo

a la consola

RunProcess(
    executable,
    args,
    s => { Console.ForegroundColor = ConsoleColor.White; Console.WriteLine(s); },
    s => { Console.ForegroundColor = ConsoleColor.Red;   Console.WriteLine(s); } 
);
3dGrabber
fuente
1

Desde PythonTR - Python Programcıları Derneği, e-kitap, örnek :

Process p = new Process();   // Create new object
p.StartInfo.UseShellExecute = false;  // Do not use shell
p.StartInfo.RedirectStandardOutput = true;   // Redirect output
p.StartInfo.FileName = "c:\\python26\\python.exe";   // Path of our Python compiler
p.StartInfo.Arguments = "c:\\python26\\Hello_C_Python.py";   // Path of the .py to be executed
livetogogo
fuente
1

Añadido process.StartInfo.**CreateNoWindow** = true;y timeout.

private static void CaptureConsoleAppOutput(string exeName, string arguments, int timeoutMilliseconds, out int exitCode, out string output)
{
    using (Process process = new Process())
    {
        process.StartInfo.FileName = exeName;
        process.StartInfo.Arguments = arguments;
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.CreateNoWindow = true;
        process.Start();

        output = process.StandardOutput.ReadToEnd();

        bool exited = process.WaitForExit(timeoutMilliseconds);
        if (exited)
        {
            exitCode = process.ExitCode;
        }
        else
        {
            exitCode = -1;
        }
    }
}
Sergei Zinovyev
fuente
Cuando lo use StandardOutput.ReadToEnd(), no volverá a la siguiente declaración hasta el final de la aplicación. ¡así que su tiempo de espera en WaitForExit (timeoutMilliseconds) no funciona! (¡su código se bloqueará!)
S.Serpooshan