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? Кстати мне очень нравится этот фрагмент кода! красиво и чисто.
— Код

3
Спасибо, но я думаю, что я не был ясен: я должен добавить еще один цикл для этого?
— код

@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 ();' может выдавать большую строку, если есть много строк вывода; пример асинхронности и ответ Ферруччо обрабатывают вывод построчно.
— Эндрю Хилл

5
Примечание: ваш первый (синхронный) подход неверен! Вы не должны одновременно читать StandardOutput и StandardError! это вызовет тупики. по крайней мере, один из них должен быть асинхронным.
— С.Серпушан

6
Process.WaitForExit () является блокировкой потоков, то есть синхронной. Не в этом суть ответа, но я подумал, что могу добавить это. Добавьте process.EnableRaisingEvents = true и используйте событие Exited для полной асинхронности.
— Том

Разве нельзя перенаправить напрямую? Я использую всю раскраску вывода sass?
— Ини

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 #.
— 17

1
если вы используете два отдельных обработчика, вы не получите взаимоблокировки
— Ovi

также в вашем примере вы читаете process.StandardOutput только один раз ... сразу после его запуска, но вам захочется читать его непрерывно во время работы процесса, не так ли?
— Ovi

7

Стандартный способ .NET сделать это - прочитать из потока StandardOutput процесса . Есть пример в связанных документах 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, консольная программа) и отобразить его стандартный вывод в форме окна:

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 и 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.