[c#] La meilleure façon d'analyser les arguments de la ligne de commande en C #?


Answers

J'aime vraiment la bibliothèque d'analyseurs de ligne de commande ( http://commandline.codeplex.com/ ). Il a une manière très simple et élégante de configurer des paramètres via des attributs:

class Options
{
    [Option("i", "input", Required = true, HelpText = "Input file to read.")]
    public string InputFile { get; set; }

    [Option(null, "length", HelpText = "The maximum number of bytes to process.")]
    public int MaximumLenght { get; set; }

    [Option("v", null, HelpText = "Print details during execution.")]
    public bool Verbose { get; set; }

    [HelpOption(HelpText = "Display this help screen.")]
    public string GetUsage()
    {
        var usage = new StringBuilder();
        usage.AppendLine("Quickstart Application 1.0");
        usage.AppendLine("Read user manual for usage instructions...");
        return usage.ToString();
    }
}
Question

Lors de la création d'applications console prenant des paramètres, vous pouvez utiliser les arguments transmis à Main(string[] args) .

Dans le passé, j'ai simplement indexé / bouclé ce tableau et fait quelques expressions régulières pour extraire les valeurs. Cependant, quand les commandes deviennent plus compliquées, l'analyse peut devenir très moche.

Donc je suis intéressé par:

  • Bibliothèques que vous utilisez
  • Les modèles que vous utilisez

Supposons que les commandes respectent toujours les normes communes telles que celles indiquées ici .







C # CLI est une bibliothèque d'analyse d'arguments de ligne de commande très simple que j'ai écrite. C'est bien documenté et open source.




Il existe de nombreuses solutions à ce problème. Pour l'exhaustivité et pour fournir l'alternative si quelqu'un désire j'ajoute cette réponse pour deux classes utiles dans ma bibliothèque de code de google .

Le premier est ArgumentList qui est responsable uniquement de l'analyse des paramètres de ligne de commande. Il recueille les paires nom-valeur définies par les commutateurs '/ x: y' ou '-x = y' et recueille également une liste d'entrées 'sans nom'. C'est l' utilisation de base est discutée ici , voir la classe ici .

La deuxième partie de ceci est le CommandInterpreter qui crée une application de ligne de commande entièrement fonctionnelle à partir de votre classe .Net. Par exemple:

using CSharpTest.Net.Commands;
static class Program
{
    static void Main(string[] args)
    {
        new CommandInterpreter(new Commands()).Run(args);
    }
    //example ‘Commands’ class:
    class Commands
    {
        public int SomeValue { get; set; }
        public void DoSomething(string svalue, int ivalue)
        { ... }

Avec l'exemple de code ci-dessus, vous pouvez exécuter ce qui suit:

Program.exe DoSomething "valeur de chaîne" 5

-- ou --

Program.exe dosomething / ivalue = 5 -svalue: "valeur de chaîne"

C'est aussi simple que cela ou aussi complexe que vous le souhaitez. Vous pouvez consulter le code source , afficher l'aide ou télécharger le fichier binaire .




C'est un gestionnaire que j'ai écrit sur la base de la classe Novell Options .

Celui-ci est destiné aux applications console qui exécutent une boucle de style while while (input !="exit") , une console interactive telle qu'une console FTP par exemple.

Exemple d'utilisation:

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();
    }
}

Et la 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;
    }
}



Je suggère la bibliothèque open-source CSharpOptParse . Il analyse la ligne de commande et hydrate un objet .NET défini par l'utilisateur avec l'entrée de ligne de commande. Je me tourne toujours vers cette bibliothèque lors de l'écriture d'une application console C #.




Une classe ad hoc très simple et facile à utiliser pour l'analyse de la ligne de commande, qui supporte les arguments par défaut.

class CommandLineArgs
{
    public static CommandLineArgs I
    {
        get
        {
            return m_instance;
        }
    }

    public  string argAsString( string argName )
    {
        if (m_args.ContainsKey(argName)) {
            return m_args[argName];
        }
        else return "";
    }

    public long argAsLong(string argName)
    {
        if (m_args.ContainsKey(argName))
        {
            return Convert.ToInt64(m_args[argName]);
        }
        else return 0;
    }

    public double argAsDouble(string argName)
    {
        if (m_args.ContainsKey(argName))
        {
            return Convert.ToDouble(m_args[argName]);
        }
        else return 0;
    }

    public void parseArgs(string[] args, string defaultArgs )
    {
        m_args = new Dictionary<string, string>();
        parseDefaults(defaultArgs );

        foreach (string arg in args)
        {
            string[] words = arg.Split('=');
            m_args[words[0]] = words[1];
        }
    }

    private void parseDefaults(string defaultArgs )
    {
        if ( defaultArgs == "" ) return;
        string[] args = defaultArgs.Split(';');

        foreach (string arg in args)
        {
            string[] words = arg.Split('=');
            m_args[words[0]] = words[1];
        }
    }

    private Dictionary<string, string> m_args = null;
    static readonly CommandLineArgs m_instance = new CommandLineArgs();
}

class Program
{
    static void Main(string[] args)
    {
        CommandLineArgs.I.parseArgs(args, "myStringArg=defaultVal;someLong=12");
        Console.WriteLine("Arg myStringArg  : '{0}' ", CommandLineArgs.I.argAsString("myStringArg"));
        Console.WriteLine("Arg someLong     : '{0}' ", CommandLineArgs.I.argAsLong("someLong"));
    }
}






Vous pouvez aimer mon Rug.Cmd

Parser d'argument de ligne de commande facile à utiliser et extensible. Handles: Bool, Plus / Minus, Chaîne, Liste de chaînes, CSV, Enumération.

Construit en '/?' mode d'aide.

Construit en '/ ??' et '/? D' modes générateur de document.

static void Main(string[] args) 
{            
    // create the argument parser
    ArgumentParser parser = new ArgumentParser("ArgumentExample", "Example of argument parsing");

    // create the argument for a string
    StringArgument StringArg = new StringArgument("String", "Example string argument", "This argument demonstrates string arguments");

    // add the argument to the parser 
    parser.Add("/", "String", StringArg);

    // parse arguemnts
    parser.Parse(args);

    // did the parser detect a /? argument 
    if (parser.HelpMode == false) 
    {
        // was the string argument defined 
        if (StringArg.Defined == true)
        {
            // write its value
            RC.WriteLine("String argument was defined");
            RC.WriteLine(StringArg.Value);
        }
    }
}

Edit: Ceci est mon projet et en tant que telle, cette réponse ne doit pas être considérée comme une approbation d'un tiers. Cela dit, je l'utilise pour chaque programme basé sur une ligne de commande que j'écris, il est open source et j'espère que d'autres pourront en bénéficier.




Je suis récemment tombé sur l'implémentation de l'analyse syntaxique de la ligne de commande FubuCore. J'aime vraiment ça, les raisons étant:

  • il est facile à utiliser - bien que je ne puisse pas trouver de documentation pour cela, la solution FubuCore fournit également un projet contenant un bel ensemble de tests unitaires qui en disent plus sur la fonctionnalité que n'importe quelle documentation.
  • il a une belle conception orientée objet, pas de répétition de code ou d'autres choses que j'avais l'habitude d'avoir dans mes applications d'analyse de ligne de commande
  • c'est déclaratif: vous écrivez essentiellement des classes pour les commandes et les ensembles de paramètres et les décorez avec des attributs pour définir différentes options (par exemple, nom, description, obligatoire / facultatif)
  • la bibliothèque imprime même un bon graphique d'utilisation, basé sur ces définitions

Voici un exemple simple sur la façon d'utiliser ceci. Pour illustrer l'utilisation, j'ai écrit un utilitaire simple qui a deux commandes: - ajouter (ajoute un objet à une liste - un objet se compose d'un nom (chaîne), valeur (int) et un drapeau booléen) - liste (listes tous les objets actuellement ajoutés)

Tout d'abord, j'ai écrit une classe Command pour la commande 'add':

[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;
    }
}

Cette commande prend une instance CommandInput en paramètre, donc je définis cela ensuite:

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; }
}

La commande suivante est 'list', qui est implémentée comme suit:

[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;
    }
}

La commande 'list' ne prend aucun paramètre, j'ai donc défini une classe NullInput pour ceci:

public class NullInput { }

Tout ce qui reste maintenant est de câbler cela dans la méthode Main (), comme ceci:

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

        var executor = new CommandExecutor(factory);

        executor.Execute(args);
    }

Le programme fonctionne comme prévu, affichant des conseils sur l'utilisation correcte dans le cas où les commandes ne sont pas valides:

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

Et un exemple d'utilisation pour la commande 'add':

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
  -------------------------------------





Related