Capturando a saída do console de um aplicativo .NET (C #)

130

Como invoco um aplicativo de console do meu aplicativo .NET e captura toda a saída gerada no console?

(Lembre-se de que não quero salvar as informações primeiro em um arquivo e, em seguida, relist como gostaria de recebê-las ao vivo.)

Gripsoft
fonte
Possível duplicata do Process.start: como obter a saída?
Michael Freidgeim
Por favor, veja as datas das duas perguntas e veja qual delas é "duplicada"
Gripsoft
"Possível duplicata" é uma maneira de limpar - fechar perguntas semelhantes e manter uma com as melhores respostas. A data não é essencial. Consulte Devo votar para fechar uma pergunta duplicada, mesmo que seja muito mais recente, e tenha respostas mais atualizadas? Se você concorda que exige esclarecimentos, vote no link Adicionar esclarecimentos ao comentário automatizado "Possível duplicata"
Michael Freidgeim

Respostas:

163

Isso pode ser facilmente alcançado usando a propriedade ProcessStartInfo.RedirectStandardOutput . Um exemplo completo está contido na documentação vinculada do MSDN; a única ressalva é que talvez seja necessário redirecionar o fluxo de erros padrão para ver toda a saída do seu aplicativo.

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
fonte
3
Se você não quiser uma nova linha extra no final, use-a Console.Write.
TM1
2
Deve-se observar que, se você usar ReadToEnd () em combinação com um aplicativo de console com capacidade para solicitar entrada do usuário. Por exemplo: Substituir arquivo: S ou N? etc Então ReadToEnd pode resultar em um vazamento de memória, pois o processo nunca sai enquanto aguarda a entrada do usuário. A maneira mais segura de capturar a saída é usar o manipulador de eventos process.OutputDataReceived e permitir que o processo notifique seu aplicativo sobre a saída a ser recebida.
Baaleos
Como capturar se, no caso de o código ser implantado no azure webapp, desde o compiler.StartInfo.FileName = "csc.exe"; pode não existir!
Asif Iqbal
Como capturar se, no caso de o código ser implantado no azure webapp, desde o compiler.StartInfo.FileName = "csc.exe"; pode não existir!
Asif Iqbal
37

Isso é um pouco melhor do que a resposta aceita do @mdb . Especificamente, também capturamos a saída de erro do processo. Além disso, capturar estas saídas através de eventos porque ReadToEnd()não funciona se você deseja capturar tanto erro e saída regular. Levei algum tempo para fazer esse trabalho, porque na verdade também requer BeginxxxReadLine()ligações depois Start().

Maneira assíncrona:

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
fonte
5
Obrigado, estou procurando isso há muito tempo!
C Bauer
3
Obrigado. Isto é perfeito.
precisa saber é o seguinte
Você terá um lugar de honra na lista de agradecimentos da minha inscrição.
marsh-wiggle
7

ConsoleAppLauncher é uma biblioteca de código aberto criada especificamente para responder a essa pergunta. Ele captura toda a saída gerada no console e fornece uma interface simples para iniciar e fechar o aplicativo do console.

O evento ConsoleOutput é acionado toda vez que uma nova linha é gravada pelo console na saída padrão / erro. As linhas são enfileiradas e garantidas para seguir a ordem de saída.

Também disponível como pacote NuGet .

Exemplo de chamada para obter a saída completa do console:

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

Amostra com feedback ao 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
fonte
2

Adicionei vários métodos auxiliares à plataforma O2 (projeto Open Source) que permitem criar facilmente uma interação com outro processo por meio da saída e entrada do console (consulte http://code.google.com/p/o2platform/ source / browse / trunk / O2_Scripts / APIs / Windows / CmdExe / CmdExeAPI.cs )

Também é útil para você a API que permite a visualização da saída do console do processo atual (em uma janela existente de controle ou pop-up). Consulte esta postagem do blog para obter mais detalhes: http://o2platform.wordpress.com/2011/11/26/api_consoleout-cs-inprocess-capture-of-the-console-output/ (este blog também contém detalhes de como consumir a saída do console de novos processos)

Dinis Cruz
fonte
Desde então, adicionei mais suporte ao uso do ConsoleOut (nesse caso, se você iniciar o processo .NET por conta própria). Dê uma olhada: Como usar a saída do console no C # REPL , Adicionando 'Console Out' para VisualStudio IDE como uma janela nativa , visualizar mensagens 'Console Out' criadas dentro UserControls
Dinis Cruz
2

Eu fiz uma versão reativa que aceita retornos de chamada para stdOut e StdErr.
onStdOute onStdErrsão chamados de forma assíncrona,
assim que os dados chegam (antes da saída do processo).

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


Exemplo de uso

O seguinte será executado executablecom argse impresso

  • stdOut em branco
  • stdErr em vermelho

para o console.

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

From 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
fonte
1

Adicionado process.StartInfo.**CreateNoWindow** = true;e 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
fonte
Quando você o usa, StandardOutput.ReadToEnd()ele não retornará à próxima instrução até o final do aplicativo. para que seu tempo limite em WaitForExit (timeoutMilliseconds) não funcione! (o seu código irá travar!)
S.Serpooshan