Language: EN

csharp-cliwrap

Simplified Process Management in C# with CliWrap

CliWrap is an open-source library for .NET that simplifies the execution and management of external processes in C# applications.

In other words, it is basically a hyper-boosted replacement for Process.Start(...) with all the process management options you can imagine.

CliWrap is designed to offer an intuitive interface that allows us to interact with the command line easily, for example, by managing command execution or capturing output.

Features of CliWrap,

  • Fluent interface: Provides a modern and fluent API for working with external processes, improving code readability and maintainability.
  • Command support: Allows for the execution of command line commands and scripts, with support for arguments and parameters.
  • Output capture: Facilitates the capture and handling of standard output and error from processes, as well as standard input.
  • Asynchronicity: Supports the asynchronous execution of commands, ideal for applications requiring non-blocking operations.
  • Flexible configuration: Offers advanced options for process configuration, including input/output redirection and timeout settings.

Installing CliWrap

To use CliWrap in your .NET project, you need to add the corresponding package via NuGet. You can do this through the Package Manager Console in Visual Studio or by using the .NET CLI. Here’s the command to install CliWrap:

Install-Package CliWrap

Using CliWrap

Running a simple command

To run a simple command line command, you can use the Cli class from CliWrap. Here’s a basic example that executes the echo command:

var result = await Cli.Wrap("cmd")
    .WithArguments(["echo", "Hello, CliWrap!"])
    .ExecuteAsync();

Console.WriteLine($"Exit Code: {result.ExitCode}");

In this example:

  • Cli.Wrap("echo"): Creates an instance of Cli for the echo command.
  • WithArguments("Hello, CliWrap!"): Sets the command arguments.
  • ExecuteAsync(): Executes the command asynchronously and waits for its completion.

Capturing output

To capture the standard and error output of a process, you can configure the output and error handlers. Here’s an example that captures the output and error of the ls command:

var result = await Cli.Wrap("cmd")    
    .WithStandardOutputPipe(PipeTarget.ToDelegate(Console.WriteLine))
    .WithStandardErrorPipe(PipeTarget.ToDelegate(Console.Error.WriteLine))
    .ExecuteAsync();

Console.WriteLine($"Exit Code: {result.ExitCode}");

In this example:

  • WithStandardOutputPipe(PipeTarget.ToDelegate(Console.WriteLine)): Redirects the standard output of the command to the delegate that writes to the console.
  • WithStandardErrorPipe(PipeTarget.ToDelegate(Console.Error.WriteLine)): Redirects the error output of the command to the delegate that writes to the error console.

Or, we can send it to a StringBuilder to have the data in memory.

var stdOutBuffer = new StringBuilder();

var result = await Cli.Wrap("cmd")
    .WithStandardOutputPipe(PipeTarget.ToDelegate(Console.WriteLine))
    .WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdOutBuffer))
    .ExecuteAsync();

Console.WriteLine($"Exit Code: {result.ExitCode}");

Handling Standard Input

CliWrap also allows sending data to the standard input of the process. We can have a process and send commands to it through standard input. Here’s an example from a string, although there are many other Pipes we can use.

var result = await Cli.Wrap("cmd")
    .WithStandardInputPipe(PipeSource.FromString("\ndir\n"))
    .WithStandardOutputPipe(PipeTarget.ToDelegate(Console.WriteLine))
    .ExecuteAsync();

Console.WriteLine($"Exit Code: {result.ExitCode}");

In this example:

  • WithStandardInputPipe(PipeSource.FromString(...): We use a string as a source of commands to send to the process. In this case, we simply send the dir command to the cmd process.

Additional Configuration

CliWrap allows for additional configuration to set timeouts, the working directory, and other process parameters. Here’s an example of how to set a timeout for the execution of a command:

using System;
using System.Threading.Tasks;
using CliWrap;

class Program
{
    static async Task Main(string[] args)
    {
        var result = await Cli.Wrap("sleep")
            .WithArguments("10")
            .WithTimeout(TimeSpan.FromSeconds(5))
            .ExecuteAsync();

        Console.WriteLine($"Exit Code: {result.ExitCode}");
    }
}

In this example:

  • WithTimeout(TimeSpan.FromSeconds(5)): Sets a timeout of 5 seconds for the sleep command.