csharp-cliwrap

Simplified Process Management in C# with CliWrap

  • 3 min

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

In other words, it’s basically a supercharged replacement for Process.Start(...) with all the process management options you can imagine.

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

Features of CliWrap,

  • Fluent Interface: Provides a modern and fluent API for working with external processes, improving code readability and maintainability.
  • Command Support: Allows 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.
  • Asynchrony: Supports asynchronous command execution, ideal for applications requiring non-blocking operations.
  • Flexible Configuration: Offers advanced options for process configuration, including input/output redirection and timeout setting.

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 using the .NET CLI. Here is 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 is a basic example that runs the echo command:

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

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

In this example:

  • Cli.Wrap("echo"): Creates a Cli instance 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 output and error of the process, you can configure the output and error handlers. Here is 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}");
Copied!

In this example:

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

Alternatively, 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}");
Copied!

Handling Standard Input

CliWrap also allows sending data to the process’s standard input. We can have a process and send commands to it through standard input. Here is an example using 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}");
Copied!

In this example:

  • WithStandardInputPipe(PipeSource.FromString(...): We use a text string as the 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 the timeout, working directory, and other process parameters. Here is an example of how to set a timeout for command execution:

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}");
    }
}
Copied!

In this example:

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