¿La mejor manera de analizar argumentos de línea de comandos en C #? [cerrado]


731

Al crear aplicaciones de consola que toman parámetros, puede usar los argumentos pasados Main(string[] args).

En el pasado, simplemente indicé / hice un bucle en esa matriz e hice algunas expresiones regulares para extraer los valores. Sin embargo, cuando los comandos se vuelven más complicados, el análisis puede volverse bastante feo.

Entonces estoy interesado en:

  • Bibliotecas que usas
  • Patrones que usas

Suponga que los comandos siempre se adhieren a estándares comunes como los que se responden aquí .


Una discusión previa, división-cadena-que contiene-línea-comando-parámetros-en-cadena-en-c # , puede tener algunas respuestas.
gimel

1
Hola, lo siento, está un poco fuera de tema. sin embargo, uso la "Configuración de la aplicación" para pasar argumentos a la aplicación. Lo encontré bastante fácil de usar y no necesito escribir argumentos / análisis de archivos, y no necesito una biblioteca adicional. msdn.microsoft.com/en-us/library/aa730869(VS.80).aspx
llámame Steve

44
@ llámame Steve: el argumento de la línea de comando es que pueden variar según la llamada. ¿Cómo lo haces con la configuración de la aplicación?
reinierpost

Respuestas:


324

Sugeriría encarecidamente usar NDesk.Options ( Documentation ) y / o Mono.Options (misma API, diferente espacio de nombres). Un ejemplo de la documentación :

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 es excelente, pero no parece admitir realmente aplicaciones de consola con más de un comando bien definido. Si lo desea, pruebe ManyConsole, que se basa en NDesk.Options: nuget.org/List/Packages/ManyConsole
Frank Schwieterman

55
Cuando tengo una aplicación con múltiples comandos distintos, "organizo" los conjuntos de opciones. Tome mdoc ( docs.go-mono.com/index.aspx?link=man%3amdoc%281%29 ), que tiene un OptionSet "global" ( github.com/mono/mono/blob/master/mcs/tools/ mdoc / ... ) que delega a un conjunto de opciones por comando (por ejemplo, github.com/mono/mono/blob/master/mcs/tools/mdoc/… )
jonp

3
NDesk no funciona para mí. Podría leer argumentos enteros bien pero no cadenas. Las variables siguen obteniendo los argumentos (por ejemplo, 's', 'a', etc.) en lugar de los valores de los argumentos (por ejemplo, 'serverName', 'ApplicationName'). Se rindió y usó 'Biblioteca de analizador de línea de comandos' en su lugar. Ok hasta ahora
Jay

2
@AshleyHenderson Por un lado, es pequeño y flexible. La mayoría de las soluciones solo funcionan con argumentos con nombre opcionales (es decir, no pueden hacer me gusta git checkout master), o sus argumentos no son flexibles (es decir, no admite --foo 123= --foo=123= -f 123= -f=123y también -v -h= -vh).
Wernight

1
@FrankSchwieterman esa debería ser su propia respuesta. Y gracias por el consejo, ManyConsole es un verdadero placer, me queda perfectamente.
quentin-starin

197

Realmente me gusta la biblioteca de analizador de línea de comandos ( http://commandline.codeplex.com/ ). Tiene una forma muy simple y elegante de configurar parámetros a través de atributos:

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

66
Esta es la biblioteca en la que me instalé también. Estoy escribiendo aplicaciones para una gran empresa que deben mantenerse durante muchos años: esta biblioteca se ha actualizado continuamente desde 2005, parece ser popular, está escrita por personas activas en la comunidad C # y tiene licencia en estilo BSD en caso de que el apoyo se desvanece.
Charles Burns el

Recomiendo esto también. Mi único problema fue: Especificar la combinación de argumentos permitidos (por ejemplo, si el argumento de movimiento debe tener argumentos de origen y destino también) puede ser posible hacerlo con los atributos. Pero quizás sea mejor hacerlo con una lógica de validación de argumento separada
Lyndon White el

1
Me gusta la clase de Opciones. Parece que también admite parámetros y marcas sin nombre --recursive.
Wernight

2
Lo acabo de probar y he implementado la opción para mi aplicación en solo unos minutos. Es extremadamente simple usar la biblioteca.
Trismegistos

3
Encontré esta biblioteca muy restrictiva para mí. Si necesita conjuntos exclusivos, no puede definir las opciones requeridas para cada conjunto, por lo que debe verificarlos manualmente. No puede definir el requisito mínimo para valores sin nombre, también debe verificarlos manualmente. El generador de pantallas de ayuda tampoco es flexible en absoluto. Si el comportamiento de la biblioteca no satisface sus necesidades de forma inmediata, no puede hacer prácticamente nada para cambiarlo.
Sergey Kostrukov

50

La biblioteca WPF TestApi viene con uno de los mejores analizadores de línea de comandos para el desarrollo de C #. Recomiendo investigarlo, desde el blog de Ivo Manolov en la 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. El análisis de la línea de comandos es algo que realmente debería provenir del proveedor (es decir, Microsoft) en lugar de a través de una herramienta de terceros, incluso si el soporte del proveedor se realiza de manera indirecta.
Joel Coehoorn

2
Dicho esto, la respuesta aceptada (mono) es la siguiente mejor opción.
Joel Coehoorn

66
@Joel, ¿qué parte es tan importante que el análisis de línea de comandos debe ser del proveedor? Cuales son tus razones?
greenoldman

3
@marcias: Creo que quiere decir que probablemente debería haber estado
listo para usar

¡La biblioteca es enorme! Contiene mucho más de lo que necesito ...
Riri

24

2
Opciones NDesk tiene una API muy agradable
user35149

2
Agregaré otro voto para NDesk, funciona bien, no es intrusivo y está bien documentado.
Terence

1
Mono.GetOptions es muy antiguo, NDesk.Options es mucho mejor (o Mono.Options si lo prefiere, es la misma clase, aquí: anonsvn.mono-project.com/source/trunk/mcs/class/Mono.Options/… )
Matt Enright

77
@ Adam Oren: ¡mi respuesta es de 1 año y 1 mes! La estructura del mono tronco fue refactorizada. Ese código ahora se coloca en anonsvn.mono-project.com/viewvc/branches/mono-2-2/mcs/class/…
abatishchev el

66
@Tormod: es Mono.GetOptions que está obsoleto, no Mono.Options. Mono.Options todavía se mantiene.
jonp

14

Parece que todo el mundo tiene sus propios analizadores de línea de comandos para mascotas, creo que es mejor que agregue el mío también :).

http://bizark.codeplex.com/

Esta biblioteca contiene un analizador de línea de comandos. que inicializará una clase con los valores de la línea de comandos. Tiene un montón de características (lo he estado construyendo durante muchos años).

De la documentación ...

El análisis de la línea de comandos en el marco de BizArk tiene estas características clave:

  • Inicialización automática: las propiedades de clase se establecen automáticamente en función de los argumentos de la línea de comandos.
  • Propiedades predeterminadas: envíe un valor sin especificar el nombre de la propiedad.
  • Conversión de valor: utiliza la potente clase ConvertEx también incluida en BizArk para convertir valores al tipo adecuado.
  • Indicadores booleanos: los indicadores se pueden especificar simplemente usando el argumento (ej, / b para verdadero y / b- para falso) o agregando el valor verdadero / falso, sí / no, etc.
  • Arreglos de argumentos: simplemente agregue varios valores después del nombre de la línea de comandos para establecer una propiedad que se define como un conjunto. Ej, / x 1 2 3 completará x con la matriz {1, 2, 3} (suponiendo que x se define como una matriz de enteros).
  • Alias ​​de línea de comando: una propiedad puede admitir múltiples alias de línea de comando para ella. Por ejemplo, ¿Ayuda usa el alias?
  • Reconocimiento de nombre parcial: no necesita deletrear el nombre completo o el alias, solo deletrea lo suficiente para que el analizador desambigue la propiedad / alias de los demás.
  • Admite ClickOnce: puede inicializar propiedades incluso cuando se especifican como la cadena de consulta en una URL para las aplicaciones implementadas de ClickOnce. El método de inicialización de la línea de comandos detectará si se está ejecutando como ClickOnce o no, por lo que su código no necesita cambiar al usarlo.
  • Crea automáticamente /? ayuda: Esto incluye un buen formato que tiene en cuenta el ancho de la consola.
  • Cargar / Guardar argumentos de línea de comandos en un archivo: Esto es especialmente útil si tiene múltiples conjuntos grandes y complejos de argumentos de línea de comandos que desea ejecutar varias veces.

2
Encontré el analizador de línea de comandos de BizArk mucho más fácil y fluido que otros. ¡Muy recomendable!
Boris Modylevsky


9

CLAP (analizador de argumentos de línea de comandos) tiene una API utilizable y está maravillosamente documentado. Haces un método, anotando los parámetros. https://github.com/adrianaisemberg/CLAP


2
Es muy simple de usar y su sitio web es genial. Sin embargo, su sintaxis no es muy intuitiva: myapp myverb -argname argvalue(debe tener -argname) o myapp -help(comúnmente --help).
Wernight

@Wernight puede usar el parámetro IsDefault en el Verbo para que pueda omitirse. No encontré soporte para los parámetros posicionales, pero solo usé parámetros posicionales cuando estaba analizando la línea de comando. Es mucho más claro usar argumentos con nombre seguidos de valores en mi humilde opinión.
Loudenvier

5

Existen numerosas soluciones a este problema. Para completar y proporcionar la alternativa si alguien lo desea, estoy agregando esta respuesta para dos clases útiles en mi biblioteca de códigos de Google .

El primero es ArgumentList, que solo es responsable de analizar los parámetros de la línea de comandos. Recopila pares de nombre-valor definidos por los modificadores '/ x: y' o '-x = y' y también recopila una lista de entradas 'sin nombre'. Su uso básico se discute aquí , vea la clase aquí .

La segunda parte de esto es CommandInterpreter, que crea una aplicación de línea de comandos completamente funcional a partir de su clase .Net. Como ejemplo:

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 el código de ejemplo anterior puede ejecutar lo siguiente:

Program.exe DoSomething "valor de cadena" 5

- o -

Program.exe dosomething / ivalue = 5 -svalue: "valor de cadena"

Es tan simple como eso o tan complejo como necesita que sea. Puede revisar el código fuente , ver la ayuda o descargar el binario .


4

Me gusta ese , porque puedes "definir reglas" para los argumentos, necesarios o no, ...

o si eres un tipo de Unix, entonces te puede gustar el puerto GNU Getopt .NET .


4

Te puede gustar mi alfombra.

Analizador de argumentos de línea de comandos fácil de usar y expandible. Manijas: Bool, Plus / Minus, String, String List, CSV, Enumeration.

Construido en '/?' modo de ayuda

Construido en '/ ??' y modos de generador de documentos '/? 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);
        }
    }
}

Editar: Este es mi proyecto y, como tal, esta respuesta no debe verse como un respaldo de un tercero. Dicho esto, lo uso para cada programa basado en la línea de comandos que escribo, es de código abierto y espero que otros puedan beneficiarse de él.


Solo para su información, debe poner un pequeño descargo de responsabilidad de que está afiliado al proyecto Rug.Cmd (como se menciona en las preguntas frecuentes): stackoverflow.com/faq#promotion - No es un gran problema ya que está promocionando un open- proyecto de origen, pero todavía es bueno agregar un descargo de responsabilidad;) +1 por cierto ... se ve bastante bien hecho.
Jason Down el

Saludos por señalar eso y gracias por el +1, me aseguraré de ser más explícito sobre mi afiliación.
Phill Tew el

No se preocupe ... hay algunos fanáticos por ahí para este tipo de cosas (no soy uno de ellos), así que me gusta avisar a la gente. Nuevamente, no suele ser un problema para proyectos de código abierto. Es principalmente para evitar que las personas envíen recomendaciones de spam para sus productos (pagados).
Jason Down el

3

Hay un analizador de argumentos de línea de comando en http://www.codeplex.com/commonlibrarynet

Puede analizar argumentos usando
1. atributos
2. llamadas explícitas
3. una sola línea de argumentos múltiples O matriz de cadenas

Puede manejar cosas como las siguientes:

- config : Qa - fecha de inicio : $ { hoy } - región : 'New York' Settings01

Es muy fácil de usar.


2

Este es un controlador que escribí basado en la Optionsclase Novell .

Este está dirigido a aplicaciones de consola que ejecutan un while (input !="exit")bucle de estilo, una consola interactiva como una consola FTP, por ejemplo.

Ejemplo de uso:

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

Y la fuente:

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

Mi favorito personal es http://www.codeproject.com/KB/recipes/plossum_commandline.aspx por 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

Recientemente me encontré con la implementación del análisis de la línea de comandos de FubuCore Me gusta mucho, las razones son:

  • es fácil de usar, aunque no pude encontrar una documentación para ello, la solución FubuCore también proporciona un proyecto que contiene un buen conjunto de pruebas unitarias que hablan más sobre la funcionalidad que cualquier documentación.
  • tiene un diseño agradable orientado a objetos, no tiene repetición de código u otras cosas que solía tener en mis aplicaciones de análisis de línea de comandos
  • es declarativo: básicamente escribe clases para los Comandos y conjuntos de parámetros y los decora con atributos para establecer varias opciones (por ejemplo, nombre, descripción, obligatorio / opcional)
  • la biblioteca incluso imprime un buen gráfico de uso, basado en estas definiciones

A continuación se muestra un ejemplo simple de cómo usar esto. Para ilustrar el uso, he escrito una utilidad simple que tiene dos comandos: - agregar (agrega un objeto a una lista - un objeto consiste en un nombre (cadena), valor (int) y una bandera booleana) - lista (listas todos los objetos agregados actualmente)

En primer lugar, escribí una clase de comando para el comando 'agregar':

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

Este comando toma una instancia de CommandInput como parámetro, así que lo defino a continuación:

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

El siguiente comando es 'list', que se implementa de la siguiente manera:

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

El comando 'list' no toma parámetros, por lo que definí una clase NullInput para esto:

public class NullInput { }

Todo lo que queda ahora es conectar esto en el método Main (), así:

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

        var executor = new CommandExecutor(factory);

        executor.Execute(args);
    }

El programa funciona como se esperaba, imprimiendo pistas sobre el uso correcto en caso de que algún comando no sea válido:

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

Y un uso de muestra para el comando 'agregar':

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 es una biblioteca de análisis de argumentos de línea de comandos muy simple que escribí. Está bien documentado y de código abierto.


¿Bien documentada? ¿Dónde está la documentación?
Suhas

Hay documentación interna (es decir, en la base de código), así como documentación externa (consulte el Readme.mkdarchivo en la Documentationcarpeta).
Bernard

Ok, comenté apresuradamente. Es posible que pueda mover su proyecto a github y su documentación comienza a aparecer automáticamente en la página de inicio.
Suhas


0

Sugeriría la biblioteca de código abierto CSharpOptParse . Analiza la línea de comando e hidrata un objeto .NET definido por el usuario con la entrada de la línea de comando. Siempre uso esta biblioteca cuando escribo una aplicación de consola C #.



0

Una clase ad hoc muy simple y fácil de usar para el análisis de la línea de comandos, que admite argumentos predeterminados.

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"));
    }
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.