简体   繁体   English

Process.start:如何获得输出?

[英]Process.start: how to get the output?

I would like to run an external command line program from my Mono/.NET app.我想从我的 Mono/.NET 应用程序运行外部命令行程序。 For example, I would like to run mencoder .例如,我想运行mencoder Is it possible:是否可以:

  1. To get the command line shell output, and write it on my text box?要获取命令行 shell 输出,并将其写在我的文本框中?
  2. To get the numerical value to show a progress bar with time elapsed?要获取数值以显示经过时间的进度条?

When you create your Process object set StartInfo appropriately:当您适当地创建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
    }
};

then start the process and read from it:然后启动该过程并从中读取:

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

You can use int.Parse() or int.TryParse() to convert the strings to numeric values.您可以使用int.Parse()int.TryParse()将字符串转换为数值。 You may have to do some string manipulation first if there are invalid numeric characters in the strings you read.如果您读取的字符串中存在无效数字字符,您可能需要先进行一些字符串操作。

You can process your output synchronously or asynchronously .您可以同步异步处理您的输出。

1. Synchronous example 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();
}

Note that it's better to process both output and errors : they must be handled separately.请注意,最好同时处理输出错误:它们必须分开处理。

(*) For some commands (here StartInfo.Arguments ) you must add the /c directive , otherwise the process freezes in the WaitForExit() . (*) 对于某些命令(此处为StartInfo.Arguments ),您必须添加/c指令,否则进程会在WaitForExit()中冻结。

2. Asynchronous example 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);
}

If you don't need to do complicate operations with the output, you can bypass the OutputHandler method, just adding the handlers directly inline:如果你不需要对输出做复杂的操作,你可以绕过 OutputHandler 方法,只需直接内联添加处理程序:

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

Alright, for anyone who wants both Errors and Outputs read, but gets deadlocks with any of the solutions, provided in other answers (like me), here is a solution that I built after reading MSDN explanation for StandardOutput property.好的,对于那些想要同时读取错误和输出但在其他答案中提供的任何解决方案(如我)陷入僵局的人来说,这是我在阅读 MSDN 对StandardOutput属性的解释后构建的解决方案。

Answer is based on T30's code:答案基于 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);
}

The standard .NET way of doing this is to read from the Process' StandardOutput stream.执行此操作的标准 .NET 方法是从 Process 的StandardOutput流中读取。 There is an example in the linked MSDN docs.链接的 MSDN 文档中有一个示例。 Similar, you can read from StandardError , and write to StandardInput .类似地,您可以从StandardError读取,并写入StandardInput

  1. It is possible to get the command line shell output of a process as described here : http://www.c-sharpcorner.com/UploadFile/edwinlima/SystemDiagnosticProcess12052005035444AM/SystemDiagnosticProcess.aspx可以获得进程的命令行 shell 输出,如下所述: http : //www.c-sharpcorner.com/UploadFile/edwinlima/SystemDiagnosticProcess12052005035444AM/SystemDiagnosticProcess.aspx

  2. This depends on mencoder.这取决于mencoder。 If it ouputs this status on the command line then yes :)如果它在命令行上输出此状态,则是 :)

you can use shared memory for the 2 processes to communicate through, check out MemoryMappedFile您可以使用共享内存为 2 个进程进行通信,请查看MemoryMappedFile

you'll mainly create a memory mapped file mmf in the parent process using "using" statement then create the second process till it terminates and let it write the result to the mmf using BinaryWriter , then read the result from the mmf using the parent process, you can also pass the mmf name using command line arguments or hard code it.您将主要使用“using”语句在父进程中创建一个内存映射文件mmf然后创建第二个进程直到它终止并让它使用BinaryWriter将结果写入mmf ,然后使用父进程从mmf读取结果,您还可以使用命令行参数或硬编码传递mmf名称。

make sure when using the mapped file in the parent process that you make the child process write the result to the mapped file before the mapped file is released in the parent process确保在父进程中使用映射文件时,您让子进程在父进程中释放映射文件之前将结果写入映射文件

Example: parent process示例:父进程

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

Child process子进程

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

to use this sample, you'll need to create a solution with 2 projects inside, then you take the build result of the child process from %childDir%/bin/debug and copy it to %parentDirectory%/bin/debug then run the parent project要使用此示例,您需要创建一个包含 2 个项目的解决方案,然后从 %childDir%/bin/debug 获取子进程的构建结果并将其复制到 %parentDirectory%/bin/debug 然后运行父项目

childDir and parentDirectory are the folder names of your projects on the pc good luck :) childDirparentDirectory是你的项目在电脑上的文件夹名称祝你好运:)

You can log process output using below code:您可以使用以下代码记录进程输出:

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

How to launch a process (such as a bat file, perl script, console program) and have its standard output displayed on a windows form:如何启动一个进程(例如 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();    

You can find ProcessCaller on this link: Launching a process and displaying its standard output您可以在此链接上找到ProcessCaller启动进程并显示其标准输出

The solution that worked for me in win and linux is the folling在 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 };
            }
        }

System.Diagnostics.Process is not the most pleasant to work with, so you may want to try CliWrap . System.Diagnostics.Process并不是最愉快的,因此您可能想尝试CliWrap It offers many different models for working with output, including piping, buffering, and real-time streaming.它提供了许多不同的输出模型,包括管道、缓冲和实时流。 Here are some examples (taken from readme).以下是一些示例(取自自述文件)。

Simply launch a command line executable:只需启动一个命令行可执行文件:

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)

Launch a command line executable and buffer stdout/stderr in-memory:启动命令行可执行文件并在内存中缓冲 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)

Launch a command line executable with manual pipe configuration:使用手动管道配置启动命令行可执行文件:

using CliWrap

var buffer = new StringBuilder();

var result = await Cli.Wrap("foo")
    .WithStandardOutputPipe(PipeTarget.ToFile("output.txt"))
    .WithStandardErrorPipe(PipeTarget.ToStringBuilder(buffer))
    .ExecuteAsync();

Launch a command line executable as an event stream:以事件流的形式启动命令行可执行文件:

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

I was running into the infamous deadlock problem when calling Process.StandardOutput.ReadLine and Process.StandardOutput.ReadToEnd .我在调用Process.StandardOutput.ReadLineProcess.StandardOutput.ReadToEnd时遇到了臭名昭著的死锁问题。

My goal/use case is simple.我的目标/用例很简单。 Start a process and redirect it's output so I can capture that output and log it to the console via .NET Core's ILogger<T> and also append the redirected output to a file log.启动一个进程并重定向它的输出,这样我就可以捕获该输出并将其通过 .NET Core 的ILogger<T>记录到控制台,并将重定向的输出附加到文件日志中。

Here's my solution using the built in async event handlers Process.OutputDataReceived and Process.ErrorDataReceived .这是我使用内置异步事件处理程序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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM