Захват вывода консоли из приложения .NET (C #)

130

Как мне вызвать консольное приложение из моего .NET-приложения и записать весь вывод, сгенерированный в консоли?

(Помните, я не хочу сначала сохранять информацию в файле, а затем повторно добавлять ее, поскольку я бы хотел получить ее как живую.)

Gripsoft
источник
Возможный дубликат Process.start: как получить результат?
Майкл Фрейджейм
Пожалуйста, посмотрите даты по обоим вопросам и посмотрите, какой из них "дублирует"
Gripsoft
«Возможный дубликат» - это способ навести порядок - закрыть похожие вопросы и оставить один с лучшими ответами. Дата не важна. См. Раздел « Должен ли я голосовать за закрытие повторяющегося вопроса, даже если он намного новее и содержит более свежие ответы?» Если вы согласны с тем, что это требует разъяснения, пожалуйста, проголосуйте за добавление уточняющей ссылки к автоматическому комментарию «Возможный дубликат»
Майкл

Ответы:

163

Этого довольно легко добиться с помощью свойства ProcessStartInfo.RedirectStandardOutput . Полный образец содержится в связанной документации MSDN; единственное предостережение: вам, возможно, придется перенаправить и стандартный поток ошибок, чтобы увидеть весь вывод вашего приложения.

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
источник
3
Если вам не нужна дополнительная новая строка в конце, просто используйте Console.Writeвместо нее.
tm1 08
2
Следует отметить, что если вы используете ReadToEnd () в сочетании с консольным приложением, которое имеет возможность запрашивать у пользователя ввод. Например: перезаписать файл: ДА или НЕТ? и т.д. Тогда ReadToEnd может привести к утечке памяти, поскольку процесс никогда не завершается, ожидая ввода данных пользователем. Более безопасный способ захвата вывода - использовать обработчик событий process.OutputDataReceived и позволить процессу уведомлять ваше приложение о получении вывода.
Baaleos
Как зафиксировать, если в случае развертывания кода в azure webapp, поскольку compiler.StartInfo.FileName = "csc.exe"; может не существует!
Асиф Икбал,
Как зафиксировать, если в случае развертывания кода в azure webapp, поскольку compiler.StartInfo.FileName = "csc.exe"; может не существует!
Асиф Икбал,
37

Это небольшое улучшение по сравнению с принятым ответом от @mdb . В частности, мы также фиксируем вывод ошибок процесса. Кроме того, мы фиксируем эти выходные данные через события, потому что ReadToEnd()это не работает, если вы хотите фиксировать как ошибки, так и обычные выходные данные. Мне потребовалось время, чтобы выполнить эту работу, потому что на самом деле также требуются BeginxxxReadLine()звонки после Start().

Асинхронный способ:

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");
}
Шитал Шах
источник
5
Спасибо, давно искал это!
C Bauer
3
Спасибо. Это потрясающе.
DrFloyd5
1
Вы получите почетное место в списке благодарностей моей заявки.
marsh-wiggle
13

Используйте ProcessInfo.RedirectStandardOutput для перенаправления вывода при создании консольного процесса.

Затем вы можете использовать Process.StandardOutput для чтения вывода программы.

Вторая ссылка содержит пример кода, как это сделать.

Франси Пенов
источник
7

ConsoleAppLauncher - это библиотека с открытым исходным кодом, созданная специально для ответа на этот вопрос. Он фиксирует весь вывод, сгенерированный в консоли, и предоставляет простой интерфейс для запуска и закрытия консольного приложения.

Событие ConsoleOutput запускается каждый раз, когда консоль записывает новую строку в стандартный вывод или вывод ошибок. Строки ставятся в очередь и гарантированно следуют порядку вывода.

Также доступен как пакет NuGet .

Пример вызова для получения полного вывода на консоль:

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

Образец с обратной связью:

// 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
источник
2

Я добавил несколько вспомогательных методов в платформу O2 (проект с открытым исходным кодом), которые позволяют легко создавать сценарии взаимодействия с другим процессом через вывод и ввод консоли (см. Http://code.google.com/p/o2platform/ источник / просмотр / транк / O2_Scripts / API / Windows / CmdExe / CmdExeAPI.cs )

Также вам может быть полезен API, который позволяет просматривать вывод на консоль текущего процесса (в существующем элементе управления или всплывающем окне). Дополнительную информацию см. В этом сообщении в блоге: http://o2platform.wordpress.com/2011/11/26/api_consoleout-cs-inprocess-capture-of-the-console-output/ (этот блог также содержит сведения о том, как потреблять консольный вывод новых процессов)

Динис Крус
источник
С тех пор я добавил дополнительную поддержку для использования ConsoleOut (в этом случае, если вы запустите процесс .NET самостоятельно). Взгляните на: Как использовать вывод консоли в C # REPL , Добавление «Console Out» в VisualStudio IDE в качестве собственного окна , Просмотр сообщений «Console Out», созданных внутри UserControls
Динис Круз,
2

Я сделал реактивную версию, которая принимает обратные вызовы для stdOut и StdErr.
onStdOutи onStdErrвызываются асинхронно,
как только данные поступают (до завершения процесса).

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


Пример использования

Следующее будет работать executableс argsи печатать

  • stdOut в белом
  • stdErr красным

к консоли.

RunProcess(
    executable,
    args,
    s => { Console.ForegroundColor = ConsoleColor.White; Console.WriteLine(s); },
    s => { Console.ForegroundColor = ConsoleColor.Red;   Console.WriteLine(s); } 
);
3dGrabber
источник
1

Из 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
источник
1

Добавлены process.StartInfo.**CreateNoWindow** = true;и 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;
        }
    }
}
Сергей Зиновьев
источник
Когда вы используете, StandardOutput.ReadToEnd()он не вернется к следующему оператору до конца приложения. так что ваш тайм-аут в WaitForExit (timeoutMilliseconds) не работает! (ваш код
зависнет