2010-02-08 11 views
17

Tengo un programa que toma varios argumentos de línea de comandos. En aras de la simplificación, diremos que tarda 3 banderas, -a, -b y -c, y utilizar el siguiente código para analizar mis argumentos:getopt no puede detectar el argumento que falta para la opción

int c; 
    while((c = getopt(argc, argv, ":a:b:c")) != EOF) 
    { 
     switch (c) 
     { 
      case 'a': 
       cout << optarg << endl; 
       break; 
      case 'b': 
       cout << optarg << endl; 
       break; 
      case ':': 
       cerr << "Missing option." << endl; 
       exit(1); 
       break; 
     } 
    } 

nota: unos, y los parámetros b tomar después de la bandera.

Pero me encuentro con un problema si invoco mi programa dicen con

./myprog -a -b parameterForB 

donde me olvidó parameterForA, la parameterForA (representado por OPTARG) se devuelve como -b y parameterForB se considera una opción sin parámetros y optind se establece en el índice de parameterForB en argv.

El comportamiento deseado en esta situación sería que ':' se devuelve después de que ningún argumento se encuentra para -a y Missing option. se imprime en el error estándar. Sin embargo, eso solo ocurre en el caso de que -a sea el último parámetro pasado al programa.

Supongo que la pregunta es: ¿hay alguna manera de hacer que getopt() suponga que no habrá opciones con -?

Respuesta

11

Véase el POSIX standard definition para getopt. Se dice que

Si [getopt] detecta una opción -argumento que falta, devolverá el carácter dos puntos (':') si el primer carácter de optstring era de dos puntos, o un cuestionable marca el carácter ('?') en caso contrario.

cuanto a que la detección,

  1. Si la opción era el último carácter de la cadena apuntada por un elemento de argv, entonces OPTARG se contener el siguiente elemento de argv, y optind se incrementará en 2. Si el valor resultante de optind es mayor que argc, esto indica que falta opción-argumento, y getopt() devolverá una indicación de error.
  2. De lo contrario, OPTARG se apuntará a la cadena siguiendo el carácter de opción en ese elemento de argv, y optind se incrementará en 1.

Parece que getopt se define por no hacer lo que querer, por lo que tiene que implementar el cheque usted mismo. Afortunadamente, puede hacerlo inspeccionando *optarg y cambiando optind usted mismo.

int c, prev_ind; 
while(prev_ind = optind, (c = getopt(argc, argv, ":a:b:c")) != EOF) 
{ 
    if (optind == prev_ind + 2 && *optarg == '-') { 
     c = ':'; 
     -- optind; 
    } 
    switch (… 
+0

Esto tiene la ventaja añadida de permitir realmente argumentos que comienzan con '-' - simplemente no pueden ir precedidos de espacios en blanco. (por ejemplo, 'my_prog -x-a') – Potatoswatter

4

Descripción completa: No soy experto en este asunto.

¿Sería útil this example de gnu.org? Parece manejar el '?' personaje en los casos en que no se suministró un argumento esperado:

while ((c = getopt (argc, argv, "abc:")) != -1) 
    switch (c) 
    { 
     case 'a': 
     aflag = 1; 
     break; 
     case 'b': 
     bflag = 1; 
     break; 
     case 'c': 
     cvalue = optarg; 
     break; 
     case '?': 
     if (optopt == 'c') 
      fprintf (stderr, "Option -%c requires an argument.\n", optopt); 
     else if (isprint (optopt)) 
      fprintf (stderr, "Unknown option `-%c'.\n", optopt); 
     else 
      fprintf (stderr, 
        "Unknown option character `\\x%x'.\n", 
        optopt); 
     return 1; 
     default: 
     abort(); 
    } 

actualización: Tal vez el siguiente funcionaría como una solución?

while((c = getopt(argc, argv, ":a:b:c")) != EOF) 
{ 
    if (optarg[0] == '-') 
    { 
     c = ':'; 
    } 
    switch (c) 
    { 
     ... 
    } 
} 
+1

El ':' al comienzo de mi cadena en la llamada a getopt() le dice que devuelva ':' en el caso de que un parámetro conocido requiera un argumento. Además, afirmé que "ese ocurre en el caso de que -a sea el último parámetro pasado al programa". Que es exactamente el caso en tu ejemplo. – finiteloop

+0

Sí, lo especificó en su pregunta. Mi culpa por no prestar suficiente atención. De todos modos, publiqué una enmienda con una breve declaración "si" que puede proporcionar el comportamiento que está buscando. ¡La mejor de las suertes! –

+0

eso parece una buena solución. gracias por la ayuda. – finiteloop

1

Hay algunas versiones muy diferentes de getopt alrededor, por lo que incluso si usted puede conseguir que funcione para una versión, es probable que haya al menos otras cinco para que romperá su solución. A menos que tengas una razón abrumadora para usar getopt, consideraría otra cosa, como Boost.Program_options.

2

Como una alternativa para proyectos sin Boost, que tienen un sencillo de sólo encabezado C envoltorio ++ para getopt (bajo la licencia BSD 3-Cláusula): https://github.com/songgao/flags.hh

Tomado de example.cc en el repositorio:

#include "Flags.hh" 

#include <cstdint> 
#include <iostream> 

int main(int argc, char ** argv) { 
    uint64_t var1; 
    uint32_t var2; 
    int32_t var3; 
    std::string str; 
    bool b, help; 

    Flags flags; 

    flags.Var(var1, 'a', "var1", uint64_t(64), "This is var1!"); 
    flags.Var(var2, 'b', "var2", uint32_t(32), "var2 haahahahaha..."); 
    flags.Var(var3, 'c', "var3", int32_t(42), "var3 is signed!", "Group 1"); 
    flags.Var(str, 's', "str", std::string("Hello!"), "This is a string, and the description is too long to fit in one line and has to be wrapped blah blah blah blah...", "Group 1"); 
    flags.Bool(b, 'd', "bool", "this is a bool variable", "Group 2"); 

    flags.Bool(help, 'h', "help", "show this help and exit", "Group 3"); 

    if (!flags.Parse(argc, argv)) { 
    flags.PrintHelp(argv[0]); 
    return 1; 
    } else if (help) { 
    flags.PrintHelp(argv[0]); 
    return 0; 
    } 

    std::cout << "var1: " << var1 << std::endl; 
    std::cout << "var2: " << var2 << std::endl; 
    std::cout << "var3: " << var3 << std::endl; 
    std::cout << "str: " << str << std::endl; 
    std::cout << "b: " << (b ? "set" : "unset") << std::endl; 

    return 0; 
} 
Cuestiones relacionadas