Best way to parse command line arguments in C#? [c

2018-12-31 02:01发布

When building console applications that take parameters, you can use the arguments passed to Main(string[] args).

In the past I've simply indexed/looped that array and done a few regular expressions to extract the values. However, when the commands get more complicated, the parsing can get pretty ugly.

So I'm interested in:

  • Libraries that you use
  • Patterns that you use

Assume the commands always adhere to common standards such as answered here.

20条回答
荒废的爱情
2楼-- · 2018-12-31 02:12

This is a handler I wrote based on the Novell Options class.

This one is aimed at console applications that execute a while (input !="exit") style loop, an interactive console such as an FTP console for example.

Example usage:

static void Main(string[] args)
{
    // Setup
    CommandHandler handler = new CommandHandler();
    CommandOptions options = new CommandOptions();

    // Add some commands. Use the v syntax for passing arguments
    options.Add("show", handler.Show)
        .Add("connect", v => handler.Connect(v))
        .Add("dir", handler.Dir);

    // Read lines
    System.Console.Write(">");
    string input = System.Console.ReadLine();

    while (input != "quit" && input != "exit")
    {
        if (input == "cls" || input == "clear")
        {
            System.Console.Clear();
        }
        else
        {
            if (!string.IsNullOrEmpty(input))
            {
                if (options.Parse(input))
                {
                    System.Console.WriteLine(handler.OutputMessage);
                }
                else
                {
                    System.Console.WriteLine("I didn't understand that command");
                }

            }

        }

        System.Console.Write(">");
        input = System.Console.ReadLine();
    }
}

And the source:

/// <summary>
/// A class for parsing commands inside a tool. Based on Novell Options class (http://www.ndesk.org/Options).
/// </summary>
public class CommandOptions
{
    private Dictionary<string, Action<string[]>> _actions;
    private Dictionary<string, Action> _actionsNoParams;

    /// <summary>
    /// Initializes a new instance of the <see cref="CommandOptions"/> class.
    /// </summary>
    public CommandOptions()
    {
        _actions = new Dictionary<string, Action<string[]>>();
        _actionsNoParams = new Dictionary<string, Action>();
    }

    /// <summary>
    /// Adds a command option and an action to perform when the command is found.
    /// </summary>
    /// <param name="name">The name of the command.</param>
    /// <param name="action">An action delegate</param>
    /// <returns>The current CommandOptions instance.</returns>
    public CommandOptions Add(string name, Action action)
    {
        _actionsNoParams.Add(name, action);
        return this;
    }

    /// <summary>
    /// Adds a command option and an action (with parameter) to perform when the command is found.
    /// </summary>
    /// <param name="name">The name of the command.</param>
    /// <param name="action">An action delegate that has one parameter - string[] args.</param>
    /// <returns>The current CommandOptions instance.</returns>
    public CommandOptions Add(string name, Action<string[]> action)
    {
        _actions.Add(name, action);
        return this;
    }

    /// <summary>
    /// Parses the text command and calls any actions associated with the command.
    /// </summary>
    /// <param name="command">The text command, e.g "show databases"</param>
    public bool Parse(string command)
    {
        if (command.IndexOf(" ") == -1)
        {
            // No params
            foreach (string key in _actionsNoParams.Keys)
            {
                if (command == key)
                {
                    _actionsNoParams[key].Invoke();
                    return true;
                }
            }
        }
        else
        {
            // Params
            foreach (string key in _actions.Keys)
            {
                if (command.StartsWith(key) && command.Length > key.Length)
                {

                    string options = command.Substring(key.Length);
                    options = options.Trim();
                    string[] parts = options.Split(' ');
                    _actions[key].Invoke(parts);
                    return true;
                }
            }
        }

        return false;
    }
}
查看更多
闭嘴吧你
3楼-- · 2018-12-31 02:12

I would suggest the open-source library CSharpOptParse. It parses the command line and hydrates a user-defined .NET object with the command-line input. I always turn to this library when writing a C# console application.

查看更多
人间绝色
4楼-- · 2018-12-31 02:13

Looks like everybody has their own pet command-line parsers, figure I had better add mine as well :).

http://bizark.codeplex.com/

This library contains a command-line parser that will initialize a class with the values from the command-line. It has a ton of features (I've been building it up over many years).

From the documentation...

Command-line parsing in the BizArk framework has these key features:

  • Automatic initialization: Class properties are automatically set based on the command-line arguments.
  • Default properties: Send in a value without specifying the property name.
  • Value conversion: Uses the powerful ConvertEx class also included in BizArk to convert values to the proper type.
  • Boolean flags: Flags can be specified by simply using the argument (ex, /b for true and /b- for false) or by adding the value true/false, yes/no, etc.
  • Argument arrays: Simply add multiple values after the command-line name to set a property that is defined as an array. Ex, /x 1 2 3 will populate x with the array { 1, 2, 3 } (assuming x is defined as an array of integers).
  • Command-line aliases: A property can support multiple command-line aliases for it. For example, Help uses the alias ?.
  • Partial name recognition: You don’t need to spell out the full name or alias, just spell enough for the parser to disambiguate the property/alias from the others.
  • Supports ClickOnce: Can initialize properties even when they are specified as the query string in a URL for ClickOnce deployed applications. The command-line initialization method will detect if it is running as ClickOnce or not so your code doesn’t need to change when using it.
  • Automatically creates /? help: This includes nice formatting that takes into account the width of the console.
  • Load/Save command-line arguments to a file: This is especially useful if you have multiple large, complex sets of command-line arguments that you want to run multiple times.
查看更多
情到深处是孤独
5楼-- · 2018-12-31 02:14

C# CLI is a very simple command-line argument parsing library that I wrote. It's well-documented and open source.

查看更多
十年一品温如言
6楼-- · 2018-12-31 02:15

CLAP (command line argument parser) has a usable API and is wonderfully documented. You make a method, annotating the parameters. https://github.com/adrianaisemberg/CLAP

查看更多
荒废的爱情
7楼-- · 2018-12-31 02:18

I recently came across The FubuCore Command line parsing implementation I really like it, the reasons being:

  • it's easy to use - although I couldn't find a documentation for it, the FubuCore solution also provides a project containing a nice set of Unit Tests that speak more about the functionality than any documentation could
  • it has a nice object oriented design, no code repetition or other such things that I used to have in my command line parsing apps
  • it's declarative: you basically write classes for the Commands and sets of parameters and decorate them with attributes to set various options (e.g. name, description, mandatory/optional)
  • the library even prints a nice Usage Graph, based on these definitions

Below is a simple example on how to use this. To illustrate the usage, I've written a simple utility that has two commands: - add (adds an object to a list - an object consists of a name(string), value(int) and a boolean flag) - list (lists all the currently added objects)

First of all, I wrote a Command class for the 'add' command:

[Usage("add", "Adds an object to the list")]
[CommandDescription("Add object", Name = "add")]
public class AddCommand : FubuCommand<CommandInput>
{
    public override bool Execute(CommandInput input)
    {
        State.Objects.Add(input); // add the new object to an in-memory collection

        return true;
    }
}

This command takes a CommandInput instance as parameter, so I define that next:

public class CommandInput
{
    [RequiredUsage("add"), Description("The name of the object to add")]
    public string ObjectName { get; set; }

    [ValidUsage("add")]
    [Description("The value of the object to add")]
    public int ObjectValue { get; set; }

    [Description("Multiply the value by -1")]
    [ValidUsage("add")]
    [FlagAlias("nv")]
    public bool NegateValueFlag { get; set; }
}

The next command is 'list', which is implemented as follows:

[Usage("list", "List the objects we have so far")]
[CommandDescription("List objects", Name = "list")]
public class ListCommand : FubuCommand<NullInput>
{
    public override bool Execute(NullInput input)
    {
        State.Objects.ForEach(Console.WriteLine);

        return false;
    }
}

The 'list' command takes no parameters, so I defined a NullInput class for this:

public class NullInput { }

All that's left now is to wire this up in the Main() method, like this:

    static void Main(string[] args)
    {
        var factory = new CommandFactory();
        factory.RegisterCommands(typeof(Program).Assembly);

        var executor = new CommandExecutor(factory);

        executor.Execute(args);
    }

The program works as expected, printing hints about the correct usage in case any commands are invalid:

  ------------------------
    Available commands:
  ------------------------
     add -> Add object
    list -> List objects
  ------------------------

And a sample usage for the 'add' command:

Usages for 'add' (Add object)
  add <objectname> [-nv]

  -------------------------------------------------
    Arguments
  -------------------------------------------------
     objectname -> The name of the object to add
    objectvalue -> The value of the object to add
  -------------------------------------------------

  -------------------------------------
    Flags
  -------------------------------------
    [-nv] -> Multiply the value by -1
  -------------------------------------
查看更多
登录 后发表回答