Il modo migliore per analizzare gli argomenti della riga di comando in C #? [chiuso]


731

Quando si creano applicazioni console che accettano parametri, è possibile utilizzare gli argomenti passati Main(string[] args).

In passato ho semplicemente indicizzato / ripetuto quell'array e fatto alcune espressioni regolari per estrarre i valori. Tuttavia, quando i comandi diventano più complicati, l'analisi può diventare piuttosto brutta.

Quindi sono interessato a:

  • Librerie che usi
  • Modelli che usi

Supponiamo che i comandi aderiscano sempre a standard comuni come la risposta qui .


Una discussione precedente, split-string-contenente-command-line-parametri-in-string-in-c # , potrebbe avere delle risposte.
Gimel

1
Ciao, scusa è un po 'fuori tema. tuttavia utilizzo le "Impostazioni applicazione" per passare l'argomento all'applicazione. L'ho trovato abbastanza facile da usare e non ho bisogno di scrivere analisi di argomenti / file e non c'è bisogno di ulteriori librerie. msdn.microsoft.com/en-us/library/aa730869(VS.80).aspx
chiamami Steve

44
@Call me Steve: il punto degli argomenti della riga di comando è che possono variare a seconda della chiamata - come si fa con le impostazioni dell'applicazione?
reinierpost,

Risposte:


324

Consiglio vivamente di utilizzare NDesk.Options ( Documentazione ) e / o Mono.Options (stessa API, spazio dei nomi diverso). Un esempio dalla documentazione :

bool show_help = false;
List<string> names = new List<string> ();
int repeat = 1;

var p = new OptionSet () {
    { "n|name=", "the {NAME} of someone to greet.",
       v => names.Add (v) },
    { "r|repeat=", 
       "the number of {TIMES} to repeat the greeting.\n" + 
          "this must be an integer.",
        (int v) => repeat = v },
    { "v", "increase debug message verbosity",
       v => { if (v != null) ++verbosity; } },
    { "h|help",  "show this message and exit", 
       v => show_help = v != null },
};

List<string> extra;
try {
    extra = p.Parse (args);
}
catch (OptionException e) {
    Console.Write ("greet: ");
    Console.WriteLine (e.Message);
    Console.WriteLine ("Try `greet --help' for more information.");
    return;
}

14
NDesk.options è eccezionale, ma non sembra davvero supportare app per console con più di un comando distinto. Se lo desideri, prova ManyConsole che si basa su NDesk.Options: nuget.org/List/Packages/ManyConsole
Frank Schwieterman,

5
Quando ho un'app con più comandi distinti, "sovrappongo" i OptionSet. Prendi mdoc ( docs.go-mono.com/index.aspx?link=man%3amdoc%281%29 ), che ha un OptionSet "globale" ( github.com/mono/mono/blob/master/mcs/tools/ mdoc /… ) che delega a un OptionSet per comando (ad esempio github.com/mono/mono/blob/master/mcs/tools/mdoc/… )
jonp

3
NDesk non ha funzionato per me. Potrebbe leggere argomenti interi ok ma non stringhe. Le variabili continuano a ottenere gli argomenti (ad es. "S", "a", ecc.) Anziché i valori degli argomenti (ad es. "Nome server", "Nome applicazione"). Abbandonati e invece usa "Command Parser Library". Ok finora.
Jay,

2
@AshleyHenderson Per prima cosa, è piccolo e flessibile. La maggior parte delle soluzioni funziona solo con argomenti nominativi opzionali (cioè, non può fare come git checkout master) o i loro argomenti non sono flessibili (ovvero, non supporta --foo 123= --foo=123= -f 123= -f=123e anche -v -h= -vh).
Wernight,

1
@FrankSchwieterman che dovrebbe essere la sua risposta. E grazie per la punta, ManyConsole è una vera delizia, si adatta perfettamente per me.
Quentin-starin

197

Mi piace molto la libreria del parser della riga di comando ( http://commandline.codeplex.com/ ). Ha un modo molto semplice ed elegante di impostare parametri tramite attributi:

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

6
Questa è anche la biblioteca su cui ho optato. Sto scrivendo applicazioni per una grande azienda che devono essere mantenute per molti anni: questa libreria è stata continuamente aggiornata dal 2005, sembra essere popolare, è scritta da persone attive nella comunità C # e ha la licenza in stile BSD nel caso il supporto svanisce.
Charles Burns,

Consiglio anche questo. Il mio unico problema era: specificare la combinazione di argomentazioni consentite (ad es. Se l'argomentazione di spostamento doveva avere anche argomenti di origine e dest) potrebbe essere possibile avere a che fare con gli attributi. Ma forse è meglio farlo con una logica di convalida Argument separata
Lyndon White,

1
Mi piace la classe Options. Sembra supportare anche parametri e flag senza nome --recursive.
Wernight,

2
L'ho appena testato e ho implementato l'opzione per la mia applicazione in pochi minuti. È estremamente semplice utilizzare la libreria.
Trismegistos,

3
Ho trovato questa biblioteca molto restrittiva per me stesso. Se hai bisogno di set esclusivi, non puoi definire le opzioni richieste per ogni set, quindi devi controllarli manualmente. Non è possibile definire i requisiti minimi per i valori senza nome, ma è necessario controllarli anche manualmente. Inoltre, lo screen builder non è affatto flessibile. Se il comportamento della libreria non si adatta alle tue esigenze, non puoi fare praticamente nulla per cambiarlo.
Sergey Kostrukov,

50

La libreria WPF TestApi include uno dei migliori parser della riga di comando per lo sviluppo di C #. Consiglio vivamente di esaminarlo, dal blog di Ivo Manolov sull'API :

// EXAMPLE #2:
// Sample for parsing the following command-line:
// Test.exe /verbose /runId=10
// This sample declares a class in which the strongly-
// typed arguments are populated
public class CommandLineArguments
{
   bool? Verbose { get; set; }
   int? RunId { get; set; }
}

CommandLineArguments a = new CommandLineArguments();
CommandLineParser.ParseArguments(args, a);

19
+1. L'analisi da riga di comando è qualcosa che dovrebbe davvero provenire dal fornitore (ad esempio Microsoft) piuttosto che tramite uno strumento di terze parti, anche se il supporto del fornitore viene fornito in modo circolare.
Joel Coehoorn,

2
Detto questo, la risposta accettata (mono) è la prossima cosa migliore.
Joel Coehoorn,

6
@Joel, quale parte è così importante che l'analisi da riga di comando deve appartenere al fornitore? Quali sono le tue ragioni?
Greenoldman,

3
@marcias: penso che probabilmente avrebbe dovuto essere Out of the Box ... come un sacco di cose :)
user7116

La biblioteca è enorme! Contiene molto più del necessario ...
Riri,

24

2
Le opzioni di NDesk hanno un'API molto bella
user35149

2
Aggiungerò un altro voto per NDesk che funziona bene, non è invadente e ben documentato.
Terence,

1
Mono.GetOptions è molto vecchio, NDesk.Options è molto più bello (o Mono.Options se preferisci, è la stessa classe, qui: anonsvn.mono-project.com/source/trunk/mcs/class/Mono.Options/… )
Matt Enright,

7
@Adam Oren: la mia risposta è di 1 anno e 1 mese! la struttura del tronco mono è stata rifattorizzata. Quel codice è ora inserito in anonsvn.mono-project.com/viewvc/branches/mono-2-2/mcs/class/…
abatishchev

6
@Tormod: è Mono.GetOptions che è obsoleto, non Mono.Options. Mono.Options è ancora mantenuto.
Jon

14

Sembra che ognuno abbia i propri parser da riga di comando per animali domestici, immagino che dovrei aggiungere anche il mio :).

http://bizark.codeplex.com/

Questa libreria contiene un parser da riga di comando che inizializza una classe con i valori dalla riga di comando. Ha un sacco di funzioni (lo sto costruendo da molti anni).

Dalla documentazione ...

L'analisi della riga di comando nel framework BizArk presenta queste caratteristiche chiave:

  • Inizializzazione automatica: le proprietà della classe vengono impostate automaticamente in base agli argomenti della riga di comando.
  • Proprietà predefinite: invia un valore senza specificare il nome della proprietà.
  • Conversione valore: utilizza la potente classe ConvertEx inclusa anche in BizArk per convertire i valori nel tipo corretto.
  • Flag booleani: i flag possono essere specificati semplicemente usando l'argomento (es, / b per true e / b- per false) o aggiungendo il valore true / false, yes / no, ecc.
  • Array di argomenti: è sufficiente aggiungere più valori dopo il nome della riga di comando per impostare una proprietà definita come un array. Es, / x 1 2 3 popolerà x con l'array {1, 2, 3} (supponendo che x sia definito come un array di numeri interi).
  • Alias ​​da riga di comando: una proprietà può supportare più alias da riga di comando. Ad esempio, Help usa l'alias?.
  • Riconoscimento parziale del nome: non è necessario precisare il nome completo o lo pseudonimo, basta scrivere abbastanza per consentire al parser di chiarire la proprietà / alias dagli altri.
  • Supporta ClickOnce: può inizializzare le proprietà anche quando sono specificate come stringa di query in un URL per le applicazioni distribuite ClickOnce. Il metodo di inizializzazione della riga di comando rileverà se è in esecuzione come ClickOnce o meno, quindi il codice non deve cambiare quando lo si utilizza.
  • Crea / / automaticamente aiuto: questo include una buona formattazione che tiene conto della larghezza della console.
  • Carica / salva argomenti della riga di comando in un file: questo è particolarmente utile se hai più serie complesse e grandi di argomenti della riga di comando che desideri eseguire più volte.

2
Ho trovato il parser da riga di comando di BizArk molto più semplice e fluente di altri. Altamente raccomandato!
Boris Modylevsky,


9

CLAP (parser di argomenti della riga di comando) ha un'API utilizzabile ed è meravigliosamente documentato. Si crea un metodo, annotando i parametri. https://github.com/adrianaisemberg/CLAP


2
È molto semplice da usare e il loro sito Web è incredibile. La loro sintassi tuttavia non è molto intuitiva: myapp myverb -argname argvalue(deve avere -argname) o myapp -help(comunemente --help).
Wernight,

@Wernight puoi usare il parametro IsDefault sul Verbo per poterlo omettere. Non ho trovato supporto per i parametri posizionali, ma ho usato i parametri posizionali solo mentre analizzavo la riga di comando. È molto più chiaro usare argomenti denominati seguiti da valori IMHO.
Loudenvier,

5

Esistono numerose soluzioni a questo problema. Per completezza e per fornire l'alternativa se qualcuno desidera, sto aggiungendo questa risposta per due classi utili nella mia libreria di codici di Google .

Il primo è ArgumentList che è responsabile solo dell'analisi dei parametri della riga di comando. Raccoglie coppie nome-valore definite dagli switch '/ x: y' o '-x = y' e raccoglie anche un elenco di voci 'senza nome'. Il suo utilizzo di base è discusso qui , guarda la classe qui .

La seconda parte di questo è CommandInterpreter che crea un'applicazione da riga di comando completamente funzionale dalla tua classe .Net. Come esempio:

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

Con il codice di esempio sopra puoi eseguire quanto segue:

Program.exe DoSomething "valore stringa" 5

-- o --

Program.exe dosomething / ivalue = 5 -svalue: "valore stringa"

È semplice o complesso quanto basta. È possibile rivedere il codice sorgente , visualizzare la guida o scaricare il file binario .


4

Mi piace quello , perché puoi "definire le regole" per gli argomenti, necessari o meno, ...

o se sei un ragazzo Unix, allora potresti apprezzare la porta GNU Getopt .NET .


4

Potrebbe piacerti il ​​mio Rug.Cmd

Analizzatore di argomenti da riga di comando facile da usare ed espandibile. Maniglie: Bool, Plus / Minus, String, String List, CSV, Enumeration.

Costruito in '/?' modalità di aiuto.

Costruito in '/ ??' e modalità generatore di documenti '/? D'.

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

Modifica: questo è il mio progetto e come tale questa risposta non dovrebbe essere vista come un'approvazione da parte di terzi. Detto questo, lo uso per ogni programma basato sulla riga di comando che scrivo, è open source ed è mia speranza che altri possano trarne beneficio.


Solo un FYI, che dovresti mettere un piccolo disclaimer che sei affiliato al progetto Rug.Cmd (come menzionato nelle FAQ): stackoverflow.com/faq#promotion - Non è un grosso problema poiché stai promuovendo un open- progetto sorgente, ma è comunque utile aggiungere un disclaimer;) +1 a proposito ... sembra abbastanza ben fatto.
Jason Down,

Saluti per averlo sottolineato e grazie per il +1, mi assicurerò di essere più esplicito sulla mia affiliazione.
Phill Tew,

Non preoccuparti ... ci sono alcuni pignoli là fuori per questo tipo di cose (io non sono uno di loro), quindi mi piace dare alla gente un avvertimento. Ancora una volta, di solito non è un problema per i progetti open source. È principalmente per impedire alle persone di inviare spamming ai loro prodotti (a pagamento).
Jason Down,

3

Esiste un parser di argomenti della riga di comando su http://www.codeplex.com/commonlibrarynet

Può analizzare gli argomenti usando
1. attributi
2. chiamate esplicite
3. singola riga di più argomenti O array di stringhe

Può gestire cose come le seguenti:

- config : Qa - data di inizio : $ { oggi } - regione : Impostazioni 'New York '01

È molto facile da usare.


2

Questo è un gestore che ho scritto basato sulla Optionsclasse Novell .

Questo è rivolto ad applicazioni console che eseguono un while (input !="exit")loop di stile, ad esempio una console interattiva come una console FTP.

Esempio di utilizzo:

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

E la fonte:

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

2

Il mio preferito è http://www.codeproject.com/KB/recipes/plossum_commandline.aspx di Peter Palotas:

[CommandLineManager(ApplicationName="Hello World",
    Copyright="Copyright (c) Peter Palotas")]
class Options
{
   [CommandLineOption(Description="Displays this help text")]
   public bool Help = false;

   [CommandLineOption(Description = "Specifies the input file", MinOccurs=1)]
   public string Name
   {
      get { return mName; }
      set
      {
         if (String.IsNullOrEmpty(value))
            throw new InvalidOptionValueException(
                "The name must not be empty", false);
         mName = value;
      }
   }

   private string mName;
}

2

Di recente mi sono imbattuto in The FubuCore Command line analizzando l'implementazione, mi piace davvero, i motivi sono:

  • è facile da usare - anche se non sono riuscito a trovare una documentazione per esso, la soluzione FubuCore fornisce anche un progetto contenente un bel set di Test unit che parlano più delle funzionalità di quanto qualsiasi documentazione potrebbe
  • ha un bel design orientato agli oggetti, nessuna ripetizione di codice o altre cose simili che avevo nella mia riga di comando per l'analisi delle app
  • è dichiarativo: fondamentalmente scrivi le classi per i Comandi e le serie di parametri e le decori con gli attributi per impostare varie opzioni (es. nome, descrizione, obbligatorio / opzionale)
  • la biblioteca stampa anche un bel grafico di utilizzo, basato su queste definizioni

Di seguito è riportato un semplice esempio su come utilizzare questo. Per illustrare l'utilizzo, ho scritto una semplice utility che ha due comandi: - aggiungi (aggiunge un oggetto a un elenco - un oggetto è composto da un nome (stringa), valore (int) e un flag booleano) - elenco (elenchi tutti gli oggetti attualmente aggiunti)

Prima di tutto, ho scritto una classe Command per il comando '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;
    }
}

Questo comando accetta un'istanza CommandInput come parametro, quindi lo definisco dopo:

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

Il comando successivo è 'list', che viene implementato come segue:

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

Il comando 'list' non accetta parametri, quindi ho definito una classe NullInput per questo:

public class NullInput { }

Ora non resta che collegarlo nel metodo Main (), in questo modo:

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

        var executor = new CommandExecutor(factory);

        executor.Execute(args);
    }

Il programma funziona come previsto, stampando suggerimenti sull'uso corretto in caso di comandi non validi:

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

E un esempio di utilizzo per il comando 'aggiungi':

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


2

C # CLI è una libreria di analisi degli argomenti della riga di comando molto semplice che ho scritto. È ben documentato e open source.


Ben documentato? Dov'è la documentazione?
Suhas

Esiste documentazione interna (cioè nella base di codice) e documentazione esterna (vedere il Readme.mkdfile nella Documentationcartella).
Bernard,

Ok, ho commentato in fretta. Forse potresti spostare il tuo progetto su github e la tua documentazione inizierà automaticamente a comparire sulla home page.
Suhas,


0

Vorrei suggerire la libreria open source CSharpOptParse . Analizza la riga di comando e idrata un oggetto .NET definito dall'utente con l'input della riga di comando. Mi rivolgo sempre a questa libreria quando scrivo un'applicazione console C #.



0

Una classe ad hoc molto semplice e facile da usare per l'analisi della riga di comando, che supporta argomenti predefiniti.

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"));
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.