Occasionally it is necessary to run a program, typically a terminal program, and capture its output.

This is possible in .NET using the Process and ProcessStartInfo classes.

Typically you would do it like this:

Log.Logger = new LoggerConfiguration()
  .WriteTo.Console()
  .CreateLogger();

var startInfo = new ProcessStartInfo
{
  FileName = "pwd",
  RedirectStandardOutput = true,
  RedirectStandardError = true,
  UseShellExecute = false,
  CreateNoWindow = true
};

using (var process = Process.Start(startInfo))
{
  var output = await process.StandardOutput.ReadToEndAsync();
  var error = await process.StandardError.ReadToEndAsync();

  await process.WaitForExitAsync();

  if (process.ExitCode == 0)
  	Log.Information(output);
  else
  	Log.Error(error);
}

As you can see, this requires quite a bit of orchestration.

In .NET 11, this has been simplified using new methods - RunAndCaptureText and its async counterpart, RunAndCaptureTextAsync, that have been added to the Process class.

The code is as follows:

Log.Logger = new LoggerConfiguration()
  .WriteTo.Console()
  .CreateLogger();

var result = await Process.RunAndCaptureTextAsync("pwd");
if (result.ExitStatus.ExitCode == 0)
	Log.Information(result.StandardOutput);
else
	Log.Error(result.StandardError);

You can see here that the code is much less.

This should return something like this:

captureSuccess

If you wanted to pass arguments to your command, you would do it like this, using the overload that takes arguments:

Log.Logger = new LoggerConfiguration()
  .WriteTo.Console()
  .CreateLogger();

var result = await Process.RunAndCaptureTextAsync("pwd", ["-l"]);
if (result.ExitStatus.ExitCode == 0)
	Log.Information(result.StandardOutput);
else
	Log.Error(result.StandardError);

This will return the following:

captureFailure

TLDR

The Process class now has new methods, RunAndCaptureText and RunAndCaptureTextAsync, for capturing output and errors from command-line/terminal applications.

The code is in my GitHub.

Happy hacking!