3

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?

Malice
  • 3,927
  • 1
  • 36
  • 52
Willy
  • 9,848
  • 22
  • 141
  • 284
  • 2
    What **is** the warning? – MakePeaceGreatAgain Apr 05 '17 at 13:34
  • @HimBromBeere It says: "Resolved file has a bad image, no metadata, or is otherwise inaccessible. Cannot load neither file or assembly "c:\....\CommandLine.dll" nor one of its dependencies. This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded". – Willy Apr 05 '17 at 13:47
  • Have you tried installing it via nuget, command for package manager console is "Install-Package CommandLineParser -Version 1.9.71" I created a 3.5 project and ran this and it appears to be working. – Bearcat9425 Apr 05 '17 at 14:01
  • @Bearcat9425 Where is package manager in visual studio 2008 professional? – Willy Apr 05 '17 at 15:54
  • I am not sure Nuget is "fully" supported in 2008 I wasn't aware you were using VS 2008. Here is an article from Scott Hanselman about getting it working on Visual Studio 2008, http://stackoverflow.com/questions/3920755/nupack-nuget-on-visual-studio-2008 – Bearcat9425 Apr 05 '17 at 15:59
  • @Bearcat9425 Ok, I will have a look, but is there any way to install command line library without nuget? – Willy Apr 05 '17 at 16:11
  • @Bearcat9425 Even If I try to add the project CommandLine35.csproj to my solution in Visual Studio 2008 Professional and then try to compile it, it fails.... – Willy Apr 06 '17 at 09:49

6 Answers6

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);            
}
Denis Voituron
  • 288
  • 1
  • 8
  • 2
    This library is not recommend for use outside .net core: See https://github.com/aspnet/Common/commit/2230370a3985fd1bbeebbdd904a3dd348f612d35 – Stuart Whelan Aug 24 '17 at 03:23
  • 1
    @StuartWhelan is correct, Microsoft.Extensions.CommandLineUtils is no longer [under active development](https://github.com/aspnet/Common/issues/257), but there's a [community-driven fork](https://github.com/natemcmaster/CommandLineUtils) from a member of the ASP.NET Core team that looks to be active at this time. – Benjamin Brandt Nov 02 '17 at 14:58
4

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.

Kevin Meier
  • 2,339
  • 3
  • 25
  • 52
2

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/dotnet/command-line-api but it's been in preview for ages.

Jamie McCrindle
  • 9,114
  • 6
  • 43
  • 48
2

Here is a simple parser i made in case you don't wanna inject a library and have a simple enough use case:

 public class CommandLineParser
    {
        private readonly List<string> _args;

        public CommandLineParser(string[] args)
        {
            _args = args.ToList();
        }

        public string? GetStringArgument(string key, char shortKey)
        {
            var index = _args.IndexOf("--" + key);

            if (index >= 0 && _args.Count > index)
            {
                return _args[index + 1];
            }

            index = _args.IndexOf("-" + shortKey);

            if (index >= 0 && _args.Count > index)
            {
                return _args[index + 1];
            }

            return null;
        }

        public bool GetSwitchArgument(bool value, char shortKey)
        {
            return _args.Contains("--" + value) || _args.Contains("-" + shortKey);
        }
    }

This supports getting a StringArgument or a SwitchArgument

Example usage:

var parser = new CommandLineParser(args);
var someKey = parser.GetStringArgument("some-key", `s`);
var someSwitch = parser.GetSwitchArgument("someSwitch", `c`);

Hope this helps

weeksdev
  • 4,265
  • 21
  • 36
1

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/dotnet/runtime/issues/68578 https://www.nuget.org/packages/System.CommandLine

Artur INTECH
  • 6,024
  • 2
  • 37
  • 34
1

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."
                }
            ),
        })
    { }

    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 : ILeafCommandHandler<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;
        }
    }
}
ebvtrnog
  • 4,167
  • 4
  • 31
  • 59