Command Line Interface(CLI) Application with .NET Core

Ali Bahraminezhad
ITNEXT
Published in
3 min readJan 2, 2020

--

A terminal (photo by Kermit Project)

The command-line interface (CLI) might look old school, but it’s one of the most useful ways to work with applications. You can interact with them in the SSH connection.

CLIs are everywhere, some of the famous ones are Angular CLI, Vue CLI, Entity Framework Core CLI, etc.

Prologue

Before you start creating CLI for your application, you need to design commands, options, arguments, well-explained guidance for it.

Think you have an application converts CSV to Excel files and backward.

Commands

  • Convert
  • Print
  • Help

Global options

  • Input file
  • Verbose

Convert sub-command options

  • The first row as header (CSV only).
  • Output name.

Print sub-command options

  • Rows
  • Tail

Examples

# For converting the CSV/Excel files
csv-util convert --first-row-header --input:file.csv --output:output_file
# For printing only the 10 first rows
csv-util print --rows:10 --input:file.csv
# For priting only the 10 tailing rows
csv-util print --rows:10 --tail --input:file.csv

We have drafted a design for our CLI. It’s time to make it real.

CLI applications in .NET

There are a couple of libraries in .NET Core for creating CLI. CommandLineUtils by Nate McMaster is one of the best in my opinion. CommandLineUtils is a fork of Microsoft.Extensions.CommandLineUtils, which is no longer under active development.

Why CommandLineUtils library?

  • It supports dependency injection in CLI application.
  • Supports attribute and API builder for creating CLI.

In this article, we focus on this library to implement CSVUtil CLI.

Getting started

Install the .NET Core SDK and VSCode if you haven’t installed it yet. Create a new console project with DOTNET.

dotnet new console -o CSVUtil

Add McMaster.Extensions.CommandLineUtils Nuget package to the project.

dotnet add package McMaster.Extensions.CommandLineUtils

Inside Program.cs file, init your CLI app like this example.

In our draft design, we have two global options: Verbose and the input file. These are options for all commands. To define an option, this library has the method Option.

  • Each option should have a template. Templates are defined as “-i| — input”.
  • Depending to the option, it can have values(or just a value) or nothing, this can be configured by CommandOptionType enum.
  • In case any option is mandatory, it can be flagged as required with IsRequired method.

Adding commands

Adding commands is the same as adding options. Please keep in mind commands can be nested.

  • Options related to the specific command can be defined inside the command.
  • While defining options, you can tell the CLI option value type in the generic method.
  • You can check if the option has value with the HasValue method.
  • In the case of using option types, the parsed value can be accessed by ParsedValue property.
  • Each command has its own help.
  • Each command should have a method OnExecuting, this method will execute in CLI.

To get your CLI ready for execution, you need to tell the app to execute CLI by the args parameter.

Execute the CLI

We have two global options and two methods. By running CSVUtil -h the app will print this to the console:

CSVUtil -h
Usage: [command] [options]
Options:
-i|--input Input file to convert
-v|--verbose Display operation details
-?|-h|--help Show help information
Commands:
convert
print
Run ' [command] -?|-h|--help' for more information about a command.

Since we defined two global options, they need to be called before [command]. For eg I want to convert a CSV file to Excel in verbose way.

CSVUtil --verbose --input:file.csv convert --first-row-header

As you can see --verbose and --input are entered before any command.

Live execution of CSVUtil

CommandLineUtils offers several other features such as Attributes and Dependency Injection. Attributes are a cool feature to create your CLI but some people like me prefer builder-pattern(Our example is implemented in builder-pattern).

using System;
using McMaster.Extensions.CommandLineUtils;

public class Program
{
public static int Main(string[] args)
=> CommandLineApplication.Execute<Program>(args);

[Option(Description = "The subject")]
public string Subject { get; }

private void OnExecute()
{
var subject = Subject ?? "world";
Console.WriteLine($"Hello {subject}!");
}
}

For dependency injection it’s good to read the project documentation.

If you like the complete and working example you can find on GitHub.

--

--

I’m a geek, a software engineer who likes to build amazing stuff 😉Here on Medium I write about different things but mostly focused on programming and .NET!