Process.start: как получить вывод?
Я хотел бы запустить внешнюю программу командной строки из моего приложения Mono/.NET. Например, я хотел бы запустить Mencoder. Является ли это возможным:
- Чтобы получить вывод оболочки командной строки и написать его в моем текстовом поле?
- Чтобы получить числовое значение для отображения индикатора выполнения по истечении времени?
11 ответов
Когда вы создаете свой 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()
преобразовать строки в числовые значения. Возможно, вам придется сначала выполнить некоторые манипуляции со строками, если в прочитанных строках есть недопустимые числовые символы.
Вы можете обрабатывать свой вывод синхронно или асинхронно.
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);
Хорошо, для любого, кто хочет прочитать и Ошибки и Выходы, но получает взаимоблокировки с любым из решений, предоставленных в других ответах (как я), вот решение, которое я построил после прочтения пояснения 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(OutputHandler);
//* 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 OutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
//* Do your stuff with the output (write to console/log/StringBuilder)
Console.WriteLine(outLine.Data);
}
Стандартный способ.NET сделать это - прочитать из потока ProcessOut StandardOutput. Есть пример в связанных документах MSDN. Похоже, вы можете читать из StandardError и записывать в StandardInput.
Вы можете использовать общую память для двух процессов, чтобы общаться через, проверить 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
имена папок ваших проектов на ПК удачи:)
Можно получить выходные данные оболочки командной строки процесса, как описано здесь: http://www.c-sharpcorner.com/UploadFile/edwinlima/SystemDiagnosticProcess12052005035444AM/SystemDiagnosticProcess.aspx
Это зависит от mencoder. Если он выводит этот статус в командной строке, тогда да:)
Вы можете зарегистрировать вывод процесса, используя следующий код:
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) {
}
Я столкнулся с печально известной проблемой тупика при звонке
Process.StandardOutput.ReadLine
а также
Process.StandardOutput.ReadToEnd
.
Моя цель / вариант использования прост. Запустите процесс и перенаправьте его вывод, чтобы я мог захватить этот вывод и записать его в консоль через .NET Core.
ILogger<T>
а также добавить перенаправленный вывод в файл журнала.
Вот мое решение с использованием встроенных обработчиков асинхронных событий
Process.OutputDataReceived
а также
Process.ErrorDataReceived
.
var p = new Process
{
StartInfo = new ProcessStartInfo(
command.FileName, command.Arguments
)
{
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
}
};
// Asynchronously pushes StdOut and StdErr lines to a thread safe FIFO queue
var logQueue = new ConcurrentQueue<string>();
p.OutputDataReceived += (sender, args) => logQueue.Enqueue(args.Data);
p.ErrorDataReceived += (sender, args) => logQueue.Enqueue(args.Data);
// Start the process and begin streaming StdOut/StdErr
p.Start();
p.BeginOutputReadLine();
p.BeginErrorReadLine();
// Loop until the process has exited or the CancellationToken is triggered
do
{
var lines = new List<string>();
while (logQueue.TryDequeue(out var log))
{
lines.Add(log);
_logger.LogInformation(log)
}
File.AppendAllLines(_logFilePath, lines);
// Asynchronously sleep for some time
try
{
Task.Delay(5000, stoppingToken).Wait(stoppingToken);
}
catch(OperationCanceledException) {}
} while (!p.HasExited && !stoppingToken.IsCancellationRequested);
Решение, которое сработало для меня в 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 };
}
}
Как запустить процесс (такой как 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
по этой ссылке: Запуск процесса и отображение его стандартного вывода
System.Diagnostics.Process
работать с ним не очень приятно, так что вы можете попробовать CliWrap. Он предлагает множество различных моделей для работы с выводом, включая конвейерную обработку, буферизацию и потоковую передачу в реальном времени. Вот несколько примеров (взяты из ридми).
Просто запустите исполняемый файл командной строки:
using CliWrap;
var result = await Cli.Wrap("path/to/exe")
.WithArguments("--foo bar")
.WithWorkingDirectory("work/dir/path")
.ExecuteAsync();
// Result contains:
// -- result.ExitCode (int)
// -- result.StartTime (DateTimeOffset)
// -- result.ExitTime (DateTimeOffset)
// -- result.RunTime (TimeSpan)
Запускаем исполняемый файл командной строки и буферизуем stdout / stderr в памяти:
using CliWrap;
using CliWrap.Buffered;
// Calling `ExecuteBufferedAsync()` instead of `ExecuteAsync()`
// implicitly configures pipes that write to in-memory buffers.
var result = await Cli.Wrap("path/to/exe")
.WithArguments("--foo bar")
.WithWorkingDirectory("work/dir/path")
.ExecuteBufferedAsync();
// Result contains:
// -- result.StandardOutput (string)
// -- result.StandardError (string)
// -- result.ExitCode (int)
// -- result.StartTime (DateTimeOffset)
// -- result.ExitTime (DateTimeOffset)
// -- result.RunTime (TimeSpan)
Запустите исполняемый файл командной строки с ручной настройкой конвейера:
using CliWrap
var buffer = new StringBuilder();
var result = await Cli.Wrap("foo")
.WithStandardOutputPipe(PipeTarget.ToFile("output.txt"))
.WithStandardErrorPipe(PipeTarget.ToStringBuilder(buffer))
.ExecuteAsync();
Запустить исполняемый файл командной строки как поток событий:
using CliWrap;
using CliWrap.EventStream;
var cmd = Cli.Wrap("foo").WithArguments("bar");
await foreach (var cmdEvent in cmd.ListenAsync())
{
switch (cmdEvent)
{
case StartedCommandEvent started:
_output.WriteLine($"Process started; ID: {started.ProcessId}");
break;
case StandardOutputCommandEvent stdOut:
_output.WriteLine($"Out> {stdOut.Text}");
break;
case StandardErrorCommandEvent stdErr:
_output.WriteLine($"Err> {stdErr.Text}");
break;
case ExitedCommandEvent exited:
_output.WriteLine($"Process exited; Code: {exited.ExitCode}");
break;
}
}