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.