Process.start: як отримати вихід?


306

Я хотів би запустити зовнішню програму командного рядка з мого додатка Mono / .NET. Наприклад, я хотів би запустити mencoder . Це можливо:

  1. Щоб отримати висновок оболонки командного рядка та записати його у моє текстове поле?
  2. Щоб отримати числове значення для відображення рядка прогресу з минулим часом?

Відповіді:


458

Коли ви створюєте Processнабір об'єктів StartInfoналежним чином:

var proc = new Process 
{
    StartInfo = new ProcessStartInfo
    {
        FileName = "program.exe",
        Arguments = "command line arguments to your executable",
        UseShellExecute = false,
        RedirectStandardOutput = true,
        CreateNoWindow = true
    }
};

потім запустіть процес і прочитайте з нього:

proc.Start();
while (!proc.StandardOutput.EndOfStream)
{
    string line = proc.StandardOutput.ReadLine();
    // do something with line
}

Ви можете використовувати int.Parse()або int.TryParse()перетворювати рядки в числові значення. Можливо, спочатку потрібно виконати маніпуляцію з рядками, якщо в прочитаних рядках є недійсні числові символи.


4
Мені було цікаво, як ти можеш мати справу з StandardError ?. До речі, мені дуже подобається цей фрагмент коду! приємно і чисто.
codea

3
Дякую, але я думаю, що мені було не зрозуміло: чи слід додати ще одну петлю для цього?
codea

@codea - бачу. Ви можете створити один цикл, який закінчується, коли обидва потоки досягають EOF. Це може стати трохи складнішим, оскільки один потік неминуче спочатку потрапить на EOF, і ви не хочете більше читати з нього. Ви також можете використовувати дві петлі у двох різних нитках.
Ферруччо

1
чи надійніше читати, поки сам процес не припиниться, а не чекати закінчення потоків?
Гусдор

@Gusdor - Я не думаю, що так. Коли процес припиняється, його потоки автоматично закриються. Також процес може закрити свої потоки задовго до його завершення.
Ферруччо

254

Ви можете обробити свій результат синхронно або асинхронно .

1. Синхронний приклад

static void runCommand()
{
    Process process = new Process();
    process.StartInfo.FileName = "cmd.exe";
    process.StartInfo.Arguments = "/c DIR"; // Note the /c command (*)
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardError = true;
    process.Start();
    //* Read the output (or the error)
    string output = process.StandardOutput.ReadToEnd();
    Console.WriteLine(output);
    string err = process.StandardError.ReadToEnd();
    Console.WriteLine(err);
    process.WaitForExit();
}

Зауважте, що краще обробляти як вихід, так і помилки : їх потрібно обробляти окремо.

(*) Для деяких команд (тут StartInfo.Arguments) ви повинні додати /c директиву , інакше процес заморожується в WaitForExit().

2. Асинхронний приклад

static void runCommand() 
{
    //* Create your Process
    Process process = new Process();
    process.StartInfo.FileName = "cmd.exe";
    process.StartInfo.Arguments = "/c DIR";
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardError = true;
    //* Set your output and error (asynchronous) handlers
    process.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);
    process.ErrorDataReceived += new DataReceivedEventHandler(OutputHandler);
    //* Start process and handlers
    process.Start();
    process.BeginOutputReadLine();
    process.BeginErrorReadLine();
    process.WaitForExit();
}

static void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine) 
{
    //* Do your stuff with the output (write to console/log/StringBuilder)
    Console.WriteLine(outLine.Data);
}

Якщо вам не потрібно робити складні операції з висновком, ви можете обійти метод OutputHandler, просто додавши обробники безпосередньо в рядок:

//* Set your output and error (asynchronous) handlers
process.OutputDataReceived += (s, e) => Console.WriteLine(e.Data);
process.ErrorDataReceived += (s, e) => Console.WriteLine(e.Data);

2
треба любити асинхронію! Мені вдалося скористатися цим кодом (трохи переписавши) на VB.net
Річард Баркер

примітка 'string output = process.StandardOutput.ReadToEnd ();' може створити великий рядок, якщо є багато рядків виводу; приклад асинхронізації та відповідь Ferruccio обидва обробляють вихідний рядок.
Ендрю Хілл

5
Примітка: ваш перший (синхронний) підхід невірний! НЕ слід читати як StandardOutput, так і StandardError синхронно! це спричинить забиття. принаймні один з них повинен бути асинхронним.
С.Серпоошан

6
Process.WaitForExit () блокує потоки, таким чином, є синхронним. Не сенс відповіді, але я думав, що можу додати це. Додайте process.EnableRaisingEvents = true і використовуйте подію, що вийшов, щоб бути повністю асинхронним.
Том

Неможливо безпосередньо перенаправити? Я використовую всю забарвлення виходу сасу?
Іні

14

Гаразд, для тих, хто хоче прочитати і помилки, і вихідні дані, але отримує тупики з будь-яким із рішень, передбачених в інших відповідях (як я), ось рішення, яке я створив після прочитання пояснення MSDN дляStandardOutput властивості.

Відповідь ґрунтується на коді T30:

static void runCommand()
{
    //* Create your Process
    Process process = new Process();
    process.StartInfo.FileName = "cmd.exe";
    process.StartInfo.Arguments = "/c DIR";
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardError = true;
    //* Set ONLY ONE handler here.
    process.ErrorDataReceived += new DataReceivedEventHandler(ErrorOutputHandler);
    //* Start process
    process.Start();
    //* Read one element asynchronously
    process.BeginErrorReadLine();
    //* Read the other one synchronously
    string output = process.StandardOutput.ReadToEnd();
    Console.WriteLine(output);
    process.WaitForExit();
}

static void ErrorOutputHandler(object sendingProcess, DataReceivedEventArgs outLine) 
{
    //* Do your stuff with the output (write to console/log/StringBuilder)
    Console.WriteLine(outLine.Data);
}

Дякуємо, що додали це. Чи можу я запитати, яку команду ви використовували?
T30

Я розробляю додаток в c #, який призначений для запуску mysqldump.exe, показує користувачеві кожне повідомлення, яке додаток генерує, чекаю, поки воно закінчиться, а потім виконую ще кілька завдань. Я не можу зрозуміти, про яку команду ти говориш? Все це питання стосується запуску процесу з c #.
Куберман

1
якщо ви користуєтесь двома окремими обробниками, ви не отримаєте тупиків
Ovi

також у прикладі ви читаєте process.StandardOutput лише один раз ... відразу після запуску, але хотілося б читати його постійно, поки процес працює, ні?
Ovi

7

Стандартний спосіб .NET зробити це - зчитування з потоку StandardOutput Process . Є приклад у пов'язаних документах MSDN. Аналогічно ви можете читати з StandardError та писати у StandardInput .



4

ви можете використовувати спільну пам’ять для двох процесів для спілкування, перевірте MemoryMappedFile

Ви в основному створите файл, відображений mmfу пам'яті в батьківському процесі, використовуючи оператор "using", потім створіть другий процес, поки він не припиниться, і дозвольте йому записати результат у mmfкористування BinaryWriter, потім прочитайте результат з mmfвикористанням батьківського процесу, ви також можете пройтиmmf ім'я, використовуючи аргументи командного рядка або жорсткий код.

переконайтесь, що при використанні відображеного файлу в батьківському процесі, що ви змушуєте дочірній процес записувати результат у відображений файл до того, як відображений файл буде випущений у батьківському процесі

Приклад: батьківський процес

    private static void Main(string[] args)
    {
        using (MemoryMappedFile mmf = MemoryMappedFile.CreateNew("memfile", 128))
        {
            using (MemoryMappedViewStream stream = mmf.CreateViewStream())
            {
                BinaryWriter writer = new BinaryWriter(stream);
                writer.Write(512);
            }

            Console.WriteLine("Starting the child process");
            // Command line args are separated by a space
            Process p = Process.Start("ChildProcess.exe", "memfile");

            Console.WriteLine("Waiting child to die");

            p.WaitForExit();
            Console.WriteLine("Child died");

            using (MemoryMappedViewStream stream = mmf.CreateViewStream())
            {
                BinaryReader reader = new BinaryReader(stream);
                Console.WriteLine("Result:" + reader.ReadInt32());
            }
        }
        Console.WriteLine("Press any key to continue...");
        Console.ReadKey();
    }

Дитячий процес

    private static void Main(string[] args)
    {
        Console.WriteLine("Child process started");
        string mmfName = args[0];

        using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting(mmfName))
        {
            int readValue;
            using (MemoryMappedViewStream stream = mmf.CreateViewStream())
            {
                BinaryReader reader = new BinaryReader(stream);
                Console.WriteLine("child reading: " + (readValue = reader.ReadInt32()));
            }
            using (MemoryMappedViewStream input = mmf.CreateViewStream())
            {
                BinaryWriter writer = new BinaryWriter(input);
                writer.Write(readValue * 2);
            }
        }

        Console.WriteLine("Press any key to continue...");
        Console.ReadKey();
    }

щоб використовувати цей зразок, вам потрібно буде створити рішення з двома проектами всередині, потім ви берете результат збірки дочірнього процесу з% childDir% / bin / debug і копіюєте його в% parentDirectory% / bin / debug, після чого запустіть команду батьківський проект

childDirі parentDirectoryназви папок ваших проектів на ПК удачі :)


1

Як запустити процес (наприклад, bat-файл, скрипт perl, консольна програма) та відобразити його стандартний вихід у формі Windows:

processCaller = new ProcessCaller(this);
//processCaller.FileName = @"..\..\hello.bat";
processCaller.FileName = @"commandline.exe";
processCaller.Arguments = "";
processCaller.StdErrReceived += new DataReceivedHandler(writeStreamInfo);
processCaller.StdOutReceived += new DataReceivedHandler(writeStreamInfo);
processCaller.Completed += new EventHandler(processCompletedOrCanceled);
processCaller.Cancelled += new EventHandler(processCompletedOrCanceled);
// processCaller.Failed += no event handler for this one, yet.

this.richTextBox1.Text = "Started function.  Please stand by.." + Environment.NewLine;

// the following function starts a process and returns immediately,
// thus allowing the form to stay responsive.
processCaller.Start();    

Ви можете знайти ProcessCallerза цим посиланням: Запуск процесу та показ його стандартного результату


1

Ви можете записати вихідний процес за допомогою наведеного нижче коду:

ProcessStartInfo pinfo = new ProcessStartInfo(item);
pinfo.CreateNoWindow = false;
pinfo.UseShellExecute = true;
pinfo.RedirectStandardOutput = true;
pinfo.RedirectStandardInput = true;
pinfo.RedirectStandardError = true;
pinfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
var p = Process.Start(pinfo);
p.WaitForExit();
Process process = Process.Start(new ProcessStartInfo((item + '>' + item + ".txt"))
{
    UseShellExecute = false,
    RedirectStandardOutput = true
});
process.WaitForExit();
string output = process.StandardOutput.ReadToEnd();
if (process.ExitCode != 0) { 
}

1

Рішення, яке працювало для мене у win and Linux, - це фолінг

// GET api/values
        [HttpGet("cifrado/{xml}")]
        public ActionResult<IEnumerable<string>> Cifrado(String xml)
        {
            String nombreXML = DateTime.Now.ToString("ddMMyyyyhhmmss").ToString();
            String archivo = "/app/files/"+nombreXML + ".XML";
            String comando = " --armor --recipient bibankingprd@bi.com.gt  --encrypt " + archivo;
            try{
                System.IO.File.WriteAllText(archivo, xml);                
                //String comando = "C:\\GnuPG\\bin\\gpg.exe --recipient licorera@local.com --armor --encrypt C:\\Users\\Administrador\\Documents\\pruebas\\nuevo.xml ";
                ProcessStartInfo startInfo = new ProcessStartInfo() {FileName = "/usr/bin/gpg",  Arguments = comando }; 
                Process proc = new Process() { StartInfo = startInfo, };
                proc.StartInfo.RedirectStandardOutput = true;
                proc.StartInfo.RedirectStandardError = true;
                proc.Start();
                proc.WaitForExit();
                Console.WriteLine(proc.StandardOutput.ReadToEnd());
                return new string[] { "Archivo encriptado", archivo + " - "+ comando};
            }catch (Exception exception){
                return new string[] { archivo, "exception: "+exception.ToString() + " - "+ comando };
            }
        }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.