的过程。开始:如何获得输出?

我想从我的Mono/运行一个外部命令行程序。网络应用程序。 例如,我想运行mencoder。是否可能:

  1. 获得命令行shell输出,并将其写在我的文本框?
  2. 获取数值以显示随着时间流逝的进度条?
385287 次浏览

标准的。net方法是从Process的StandardOutput流中读取。在链接的MSDN文档中有一个例子。类似地,你可以从StandardError读取,并写入StandardInput

  1. 可以得到一个进程的命令行shell输出,如下所示:http://www.c-sharpcorner.com/UploadFile/edwinlima/SystemDiagnosticProcess12052005035444AM/SystemDiagnosticProcess.aspx

  2. 这取决于mencoder。如果它在命令行输出这个状态,那么是:)

当你适当地创建你的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(). c中冻结。

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

你可以为这两个进程使用共享内存进行通信,检查MemoryMappedFile

你将主要使用"using"语句在父进程中创建一个内存映射文件mmf,然后创建第二个进程,直到它终止,并让它使用BinaryWriter将结果写入mmf,然后使用父进程从mmf读取结果,你也可以使用命令行参数传递mmf名称或硬编码它。

当在父进程中使用映射文件时,请确保在父进程释放映射文件之前,让子进程将结果写入映射文件

< p >的例子: 父进程< / p >
    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();
}

要使用这个示例,您需要创建一个包含2个项目的解决方案,然后从%childDir%/bin/debug中获取子进程的构建结果,并将其复制到%parentDirectory%/bin/debug中,然后运行父项目

childDirparentDirectory是你的项目在pc上的文件夹名 祝你好运:)

好吧,对于任何想要读取错误和输出,但在其他答案中提供的任何解决方案中获得死锁的人(比如我),这里是我在阅读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);
}

如何启动一个进程(如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: 启动进程并显示其标准输出

您可以使用以下代码记录流程输出:

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

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

在调用Process.StandardOutput.ReadLineProcess.StandardOutput.ReadToEnd时,我遇到了臭名昭著的死锁问题。

我的目标/用例很简单。启动一个进程并重定向它的输出,这样我就可以捕获该输出并通过. net Core的ILogger<T>将其记录到控制台,并将重定向的输出附加到文件日志中。

下面是我使用内置异步事件处理程序Process.OutputDataReceivedProcess.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);

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