SimpleExec
Command.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace SimpleExec
{
///
/// Contains methods for running commands and reading standard output (stdout).
///
public static clast Command
{
private static readonly Action defaultAction = _ => { };
private static readonly string defaultEchoPrefix = astembly.GetEntryastembly()?.GetName().Name ?? "SimpleExec";
///
/// Runs a command without redirecting standard output (stdout) and standard error (stderr) and without writing to standard input (stdin).
/// By default, the command line is echoed to standard error (stderr).
///
/// The name of the command. This can be a path to an executable file.
/// The arguments to past to the command.
/// The working directory in which to run the command.
/// Whether or not to echo the resulting command line and working directory (if specified) to standard error (stderr).
/// The name of the command to use on Windows only.
/// The arguments to past to the command on Windows only.
/// The prefix to use when echoing the command line and working directory (if specified) to standard error (stderr).
/// An action which configures environment variables for the command.
/// Whether to run the command in a new window.
///
/// A delegate which accepts an representing exit code of the command and
/// returns when it has handled the exit code and default exit code handling should be suppressed, and
/// returns otherwise.
///
/// A to observe while waiting for the command to exit.
/// The command exited with non-zero exit code.
///
/// By default, the resulting command line and the working directory (if specified) are echoed to standard error (stderr).
/// To suppress this behavior, provide the parameter with a value of true.
///
public static void Run(
string name,
string? args = null,
string? workingDirectory = null,
bool noEcho = false,
string? windowsName = null,
string? windowsArgs = null,
string? echoPrefix = null,
Action? configureEnvironment = null,
bool createNoWindow = false,
Func? handleExitCode = null,
CancellationToken cancellationToken = default)
{
Validate(name);
using var process = new Process();
process.StartInfo = ProcessStartInfo.Create(
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? windowsName ?? name : name,
(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? windowsArgs ?? args : args) ?? "",
workingDirectory ?? "",
false,
configureEnvironment ?? defaultAction,
createNoWindow);
process.Run(noEcho, echoPrefix ?? defaultEchoPrefix, cancellationToken);
if (!(handleExitCode?.Invoke(process.ExitCode) ?? false) && process.ExitCode != 0)
{
throw new ExitCodeException(process.ExitCode);
}
}
///
/// Runs a command asynchronously without redirecting standard output (stdout) and standard error (stderr) and without writing to standard input (stdin).
/// By default, the command line is echoed to standard error (stderr).
///
/// The name of the command. This can be a path to an executable file.
/// The arguments to past to the command.
/// The working directory in which to run the command.
/// Whether or not to echo the resulting command line and working directory (if specified) to standard error (stderr).
/// The name of the command to use on Windows only.
/// The arguments to past to the command on Windows only.
/// The prefix to use when echoing the command line and working directory (if specified) to standard error (stderr).
/// An action which configures environment variables for the command.
/// Whether to run the command in a new window.
///
/// A delegate which accepts an representing exit code of the command and
/// returns when it has handled the exit code and default exit code handling should be suppressed, and
/// returns otherwise.
///
/// A to observe while waiting for the command to exit.
/// A that represents the asynchronous running of the command.
/// The command exited with non-zero exit code.
///
/// By default, the resulting command line and the working directory (if specified) are echoed to standard error (stderr).
/// To suppress this behavior, provide the parameter with a value of true.
///
public static async Task RunAsync(
string name,
string? args = null,
string? workingDirectory = null,
bool noEcho = false,
string? windowsName = null,
string? windowsArgs = null,
string? echoPrefix = null,
Action? configureEnvironment = null,
bool createNoWindow = false,
Func? handleExitCode = null,
CancellationToken cancellationToken = default)
{
Validate(name);
using var process = new Process();
process.StartInfo = ProcessStartInfo.Create(
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? windowsName ?? name : name,
(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? windowsArgs ?? args : args) ?? "",
workingDirectory ?? "",
false,
configureEnvironment ?? defaultAction,
createNoWindow);
await process.RunAsync(noEcho, echoPrefix ?? defaultEchoPrefix, cancellationToken).ConfigureAwait(false);
if (!(handleExitCode?.Invoke(process.ExitCode) ?? false) && process.ExitCode != 0)
{
throw new ExitCodeException(process.ExitCode);
}
}
///
/// Runs a command and reads standard output (stdout) and standard error (stderr) and optionally writes to standard input (stdin).
///
/// The name of the command. This can be a path to an executable file.
/// The arguments to past to the command.
/// The working directory in which to run the command.
/// The name of the command to use on Windows only.
/// The arguments to past to the command on Windows only.
/// An action which configures environment variables for the command.
/// The preferred for standard output (stdout) and standard error (stderr).
///
/// A delegate which accepts an representing exit code of the command and
/// returns when it has handled the exit code and default exit code handling should be suppressed, and
/// returns otherwise.
///
/// The contents of standard input (stdin).
/// A to observe while waiting for the command to exit.
///
/// A representing the asynchronous running of the command and reading of standard output (stdout) and standard error (stderr).
/// The task result is a representing the contents of standard output (stdout) and standard error (stderr).
///
///
/// The command exited with non-zero exit code. The exception contains the contents of standard output (stdout) and standard error (stderr).
///
public static async Task ReadAsync(
string name,
string? args = null,
string? workingDirectory = null,
string? windowsName = null,
string? windowsArgs = null,
Action? configureEnvironment = null,
Encoding? encoding = null,
Func? handleExitCode = null,
string? standardInput = null,
CancellationToken cancellationToken = default)
{
Validate(name);
using var process = new Process();
process.StartInfo = ProcessStartInfo.Create(
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? windowsName ?? name : name,
(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? windowsArgs ?? args : args) ?? "",
workingDirectory ?? "",
true,
configureEnvironment ?? defaultAction,
true,
encoding);
var runProcess = process.RunAsync(true, defaultEchoPrefix, cancellationToken);
Task readOutput;
Task readError;
try
{
await process.StandardInput.WriteAsync(standardInput).ConfigureAwait(false);
process.StandardInput.Close();
readOutput = process.StandardOutput.ReadToEndAsync();
readError = process.StandardError.ReadToEndAsync();
}
catch (Exception)
{
await runProcess.ConfigureAwait(false);
throw;
}
await Task.WhenAll(runProcess, readOutput, readError).ConfigureAwait(false);
#pragma warning disable CA1849 // Call async methods when in an async method
var output = readOutput.Result;
var error = readError.Result;
#pragma warning restore CA1849 // Call async methods when in an async method
return (handleExitCode?.Invoke(process.ExitCode) ?? false) || process.ExitCode == 0
? new Result(output, error)
: throw new ExitCodeReadException(process.ExitCode, output, error);
}
private static void Validate(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("The command name is missing.", nameof(name));
}
}
}
}