2008-09-10 8 views
38

Si está escribiendo un programa que es ejecutable desde la línea de comando, a menudo desea ofrecer al usuario varias opciones o indicadores, junto posiblemente con más de un argumento. He tropezado a través de esto muchas veces, pero ¿hay algún tipo de patrón de diseño para pasar por args y girar las funciones apropiadas?Cuál es el patrón de diseño para procesar los argumentos de línea de comando

considerar:

myprogram -f filename -d directory -r regex 

¿Cómo se organiza el código después de recuperar los argumentos utilizando cualquier muebles empotrados para su idioma? (las respuestas específicas al idioma son bienvenidas, si eso ayuda a articular una respuesta)

+0

Esto debe etiquetarse como "independiente del idioma", ya que la pregunta es solicitar un patrón de diseño, así como sugerencias. – martinatime

Respuesta

13

No conozco ningún "patrón" documentado para el procesamiento.

Creo que una de las bibliotecas/API más antiguas para manejar argumentos es getopt. Google "getopt" muestra muchas páginas man y enlaces a implementaciones.

Generalmente, tengo un servicio de preferencias o configuraciones en mi aplicación que el procesador de argumentos sabe cómo comunicarse. Los argumentos se traducen en algo en este servicio que la aplicación luego consulta. Esto podría ser tan simple como un diccionario de configuraciones (como una configuración de cadena llamada "nombre de archivo").

1

Recomendaría utilizar una biblioteca de procesador de línea de comandos. Some Russian guy creó uno decente, pero hay toneladas de ellos por ahí. Le ahorrará algo de tiempo para que pueda concentrarse en el propósito de su aplicación en lugar de analizar los interruptores de línea de comando.

+0

Me gusta este artículo. ¡Gracias! :-) – craigmoliver

5

No mencionó el idioma, pero para Java nos ha encantado Apache Commons CLI. Para C/C++, getopt.

0

uso el Getopts::std y Getopts::long en Perl y también la función Getopt en C. Este estandariza el análisis y el formato de parámetros. Otros idiomas tienen diferentes mecanismos para manejarlos.

Esperanza esto ayuda

0

El diseño estándar suele seguir a lo getopt hace, hay bibliotecas getopt para muchos idiomas, .NET, Python, C, Perl, PHP, etc.

El diseño básico es tener un analizador de línea de comando que devuelve parte por parte los argumentos pasados ​​para ser marcados en un bucle.

This El artículo lo analiza con más detalle.

2

La biblioteca boost::program_options es agradable si estás en C++ y tienen el lujo de usar Boost.

+0

Es una buena opción. Esa biblioteca es un poco compleja en comparación con el antiguo getopt, pero también permite usar archivos de configuración (sustituyendo o integrando argumentos de línea de comando). –

0

No estoy tan interesado en las bibliotecas, aunque eso definitivamente es útil.Buscaba más un "pseudo código" que ilustra el procesamiento de, por ejemplo, el conjunto de banderas promedio y un grupo de argumentos más largos.

+0

Es posible que desee actualizar esto en su pregunta en lugar de aquí sobre la aclaración. Solo es para respuestas. – rpattabi

2

Suponiendo que tiene un objeto "config" que desea configurar con las banderas y un analizador de línea de comandos adecuado que se encarga de analizar la línea de comandos y proporcionar un flujo constante de las opciones, aquí va un bloque de pseudocódigo

while (current_argument = cli_parser_next()) { 
    switch(current_argument) { 
     case "f": //Parser strips the dashes 
     case "force": 
      config->force = true; 
      break; 
     case "d": 
     case "delete": 
      config->delete = true; 
      break; 
     //So on and so forth 
     default: 
      printUsage(); 
      exit; 
    } 
} 
2

Prefiero opciones como "-t text" y "-i 44"; No me gusta "-fname" o "--very-long-argument = some_value".

Y "-?", "-h" y "/ h" producen una pantalla de ayuda.

Así es como se ve mi código:

int main (int argc, char *argv[]) 
    { int i; 
     char *Arg; 
     int ParamX, ParamY; 
     char *Text, *Primary; 

    // Initialize... 
    ParamX = 1; 
    ParamY = 0; 
    Text = NULL; 
    Primary = NULL; 

    // For each argument... 
    for (i = 0; i < argc; i++) 
     { 
     // Get the next argument and see what it is 
     Arg = argv[i]; 
     switch (Arg[0]) 
     { 
     case '-': 
     case '/': 
      // It's an argument; which one? 
      switch (Arg[1]) 
       { 
       case '?': 
       case 'h': 
       case 'H': 
        // A cry for help 
        printf ("Usage: whatever...\n\n"); 
        return (0); 
        break; 

       case 't': 
       case 'T': 
        // Param T requires a value; is it there? 
        i++; 
        if (i >= argc) 
        { 
        printf ("Error: missing value after '%s'.\n\n", Arg); 
        return (1); 
        } 

        // Just remember this 
        Text = Arg; 

        break; 

       case 'x': 
       case 'X': 
        // Param X requires a value; is it there? 
        i++; 
        if (i >= argc) 
        { 
        printf ("Error: missing value after '%s'.\n\n", Arg); 
        return (1); 
        } 

        // The value is there; get it and convert it to an int (1..10) 
        Arg = argv[i]; 
        ParamX = atoi (Arg); 
        if ((ParamX == 0) || (ParamX > 10)) 
        { 
        printf ("Error: invalid value for '%s'; must be between 1 and 10.\n\n", Arg); 
        return (1); 
        } 

        break; 

       case 'y': 
       case 'Y': 
        // Param Y doesn't expect a value after it 
        ParamY = 1; 
        break; 

       default: 
        // Unexpected argument 
        printf ("Error: unexpected parameter '%s'; type 'command -?' for help.\n\n", Arg); 
        return (1); 
        break; 
       } 

      break; 

     default: 
      // It's not a switch that begins with '-' or '/', so it's the primary option 
      Primary = Arg; 

      break; 
     } 
     } 

    // Done 
    return (0); 
    } 
4

algunos comentarios sobre este ...

En primer lugar, mientras que no hay patrones per se, escribiendo un programa de análisis es esencialmente un ejercicio mecánico, dado que se le ha dado una gramática, se puede generar fácilmente un analizador sintáctico. Herramientas como Bison y ANTLR vienen a la mente.

Dicho esto, los generadores de analizadores suelen ser excesivos para la línea de comandos. Entonces, el patrón habitual es escribir uno usted mismo (como han demostrado otros) un par de veces hasta que se canse de lidiar con el tedioso detalle y encuentre una biblioteca que lo haga por usted.

escribí una para C++ que ahorra un montón de esfuerzo que getopt imparte y hace buen uso de plantillas: TCLAP

+0

'' TCLAP'' es una fantástica biblioteca de análisis CLI. La configuración preliminar de las reglas y la extracción posterior al análisis del '' argv'' es muy útil, intuitiva y ayuda a dividir el programa en los componentes discretos correctos (en mi humilde opinión). – Sean

2

estoy riffs sobre la respuesta antlr por mes5k. Este link to Codeproject es para un artículo que analiza ANLTR y usa el patrón de visita para implementar las acciones que desea que tome su aplicación. Está bien escrito y vale la pena revisarlo.

12

Creo que la siguiente respuesta es más en la línea de lo que busca:

Usted debe mirar aplicar el patrón de plantilla (Template Method en "Design Patterns" [Gamma, et al])

En pocas palabras es el procesamiento global es el siguiente:

If the arguments to the program are valid then 
    Do necessary pre-processing 
    For every line in the input 
     Do necessary input processing 
    Do necessary post-processing 
Otherwise 
    Show the user a friendly usage message 

En resumen, implementar una clase ConsoleEngineBase que tiene métodos para:

PreProcess() 
ProcessLine() 
PostProcess() 
Usage() 
Main() 

A continuación, cree un chasis, que crea una instancia de ConsoleEngine() y envía el mensaje Main() para iniciarlo.

Para ver un buen ejemplo de cómo aplicar esto a un programa de línea de la consola o el comando revisa el siguiente enlace: http://msdn.microsoft.com/en-us/magazine/cc164014.aspx

El ejemplo está en C#, pero las ideas son fáciles de implementar en cualquier otro entorno.

Consideraría el GetOpt() simplemente como la parte que encaja en el manejo de argumentos (preprocesamiento).

Espero que esto ayude.

+1

upvoted por apegarse a los conceptos en lugar de la implementación. Siento que esta debería haber sido la respuesta elegida. – Plasmarob

3

Bueno, es una publicación anterior pero me gustaría contribuir.La pregunta estaba dirigida a la elección de los patrones de diseño; sin embargo, pude ver mucha discusión sobre qué biblioteca usar. He revisado el enlace de Microsoft según Lindsay, que habla sobre el patrón de diseño de la plantilla a usar.

Sin embargo, no estoy convencido con la publicación. La intención del patrón de la plantilla es definir una plantilla que será implementada por varias otras clases para tener un comportamiento uniforme. No creo que el análisis de la línea de comandos encaje.

Preferiría ir con el patrón de diseño "Comando". Este patrón es el más adecuado para las opciones de menú.

http://www.blackwasp.co.uk/Command.aspx

por lo que en su caso, -F, -d y -R todo se convierte en comandos que tiene el receptor común o separada definida. De esa forma se pueden definir más receptores en el futuro. El siguiente paso será encadenar estas responsabilidades de comandos, en caso de que se requiera una cadena de procesamiento. Por lo que elegiría.

http://www.blackwasp.co.uk/ChainOfResponsibility.aspx

supongo que la combinación de estos dos son la mejor manera de organizar el código para el procesamiento de línea de comandos o cualquier enfoque controlado por menú.

Cuestiones relacionadas