简体   繁体   中英

Parse command line arguments/options in C#

I have a console application with some arguments and options so I would like to use a free third-party library.

I have found two libraries for this purpose: NDesk.Options and Command Line Parser Library

Finally I have decided to use Command Line Parser Library because it is clearer using properties so I have downloaded it and added a reference to it.

The problem is that when adding the reference to my .NET Framework 3.5 project I get a warning icon. From the above page where I have downloaded it, it says that compatibility is .NET Framework 3.5+ so I understand 3.5 is compatible, am I right? If not which previous version of it is compatible with .NET Framework 3.5?

You can also use the new Microsoft CommandLineUtils library. The nuget package is here, but only for .NET Core or Framrwork 4.5.2. But you can download the source code (only 7 files) and include in your projet. For the Framework 3.5, you have only 2 compilation errors to solve: remove an extra method (using Tasks) and remove one line (in HandleUnexpectedArg).

To use this library, find here a first sample:

static void Main(string[] args)
{
    var cmd = new CommandLineApplication();
    var argAdd = cmd.Option("-a | --add <value>", "Add a new item", CommandOptionType.SingleValue);

    cmd.OnExecute(() =>
    {
        Console.WriteLine(argAdd.Value());
        return 0;
    });

    cmd.HelpOption("-? | -h | --help");
    cmd.Execute(args);            
}

McMaster.Extensions.CommandLineUtils is the best command line parser for c# that I've used. I especially like that it supports subcommands well.

Source code is here: https://github.com/natemcmaster/CommandLineUtils

dotnet add package McMaster.Extensions.CommandLineUtils

This is a simple example of how to use it using attributes:

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; } = "world";

    [Option(ShortName = "n")]
    public int Count { get; } = 1;

    private void OnExecute()
    {
        for (var i = 0; i < Count; i++)
        {
            Console.WriteLine($"Hello {Subject}!");
        }
    }
}

Or you could use a builder:

using System;
using McMaster.Extensions.CommandLineUtils;

var app = new CommandLineApplication();

app.HelpOption();

var subject = app.Option("-s|--subject <SUBJECT>", "The subject", CommandOptionType.SingleValue);
subject.DefaultValue = "world";

var repeat = app.Option<int>("-n|--count <N>", "Repeat", CommandOptionType.SingleValue);
repeat.DefaultValue = 1;

app.OnExecute(() =>
{
    for (var i = 0; i < repeat.ParsedValue; i++)
    {
        Console.WriteLine($"Hello {subject.Value()}!");
    }
});

return app.Execute(args);

Microsoft have also been working on a command line parser: https://github.com/do.net/command-line-api but it's been in preview for ages.

I recommend FluentArgs (see: https://github.com/kutoga/FluentArgs ). I think it is very easy to use:

namespace Example
{
    using System;
    using System.Threading.Tasks;
    using FluentArgs;

    public static class Program
    {
        public static Task Main(string[] args)
        {
            return FluentArgsBuilder.New()
                .DefaultConfigsWithAppDescription("An app to convert png files to jpg files.")
                .Parameter("-i", "--input")
                    .WithDescription("Input png file")
                    .WithExamples("input.png")
                    .IsRequired()
                .Parameter("-o", "--output")
                    .WithDescription("Output jpg file")
                    .WithExamples("output.jpg")
                    .IsRequired()
                .Parameter<ushort>("-q", "--quality")
                    .WithDescription("Quality of the conversion")
                    .WithValidation(n => n >= 0 && n <= 100)
                    .IsOptionalWithDefault(50)
                .Call(quality => outputFile => inputFile =>
                {
                    /* ... */
                    Console.WriteLine($"Convert {inputFile} to {outputFile} with quality {quality}...");
                    /* ... */
                    return Task.CompletedTask;
                })
                .ParseAsync(args);
        }
    }
}

There are many other examples on the github page.

System.CommandLine might do the trick. Though as of November 2022 it is still in beta. I suppose the .NET team is going to include it in some upcoming .NET framework release.

https://github.com/do.net/runtime/issues/68578 https://www.nuget.org/packages/System.CommandLine

If you're looking for a third-party library to help you parse command-line arguments and options in C#, you might want to check out the TreeBasedCli library. It is a C# library designed to simplify the process of creating command-line interfaces (CLIs) with nested subcommands, and offers a number of benefits for both developers and users.

One of the key features of TreeBasedCli is its modular structure, which allows you to easily organize and structure your CLI's functionality using leaf and branch commands. Leaf commands represent specific actions that can be performed, and are implemented as individual classes with their own command definition, input parser, and asynchronous handler. Branch commands, on the other hand, represent a group of subcommands and do not have an associated action. This allows you to easily create complex CLIs with multiple levels of nesting.

Another benefit of TreeBasedCli is its support for asynchronous command execution. It also includes a lightweight Dependency Injection (DI) interface, allowing you to use your preferred method of DI type resolution.

public class CreateCatCommand :
    LeafCommand<
        CreateCatCommand.Arguments,
        CreateCatCommand.Parser,
        CreateCatCommand.Handler>
{
    private const string NameLabel = "--name";

    public CreateCatCommand() : base(
        label: "create-cat",
        description: new[]
        {
            "Prints out a cat."
        },
        options: new[]
        {
            new CommandOption(
                label: NameLabel,
                description: new[]
                {
                    "Required. The name of the cat to print."
                }
            ),
        },
        DependencyInjectionService.Instance)
    { }

    public record Arguments(string CatName) : IParsedCommandArguments;

    public class Parser : ICommandArgumentParser<Arguments>
    {
        public IParseResult<Arguments> Parse(CommandArguments arguments)
        {
            string name = arguments.GetArgument(NameLabel).ExpectedAsSingleValue();

            var result = new Arguments(
                CatName: name
            );

            return new SuccessfulParseResult<Arguments>(result);
        }
    }

    public class Handler : ICommandHandler<Arguments>
    {
        private readonly IUserInterface userInterface;

        public Handler(IUserInterface userInterface)
        {
            this.userInterface = userInterface;
        }

        public Task HandleAsync(Arguments arguments, LeafCommand _)
        {
            this.userInterface.WriteLine($"I am a cat 😸 with the name {arguments.CatName}!");
            return Task.CompletedTask;
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM