2009-06-27 14 views
37

En C, getopt_long no analiza los argumentos opcionales para los parámetros de línea de comando.getopt no analiza argumentos opcionales para los parámetros

Cuando ejecuto el programa, el argumento opcional no se reconoce como el ejemplo que se muestra a continuación.

$ ./respond --praise John 
Kudos to John 
$ ./respond --blame John 
You suck ! 
$ ./respond --blame 
You suck ! 

Aquí está el código de prueba.

#include <stdio.h> 
#include <getopt.h> 

int main(int argc, char ** argv) 
{ 
    int getopt_ret, option_index; 
    static struct option long_options[] = { 
       {"praise", required_argument, 0, 'p'}, 
       {"blame", optional_argument, 0, 'b'}, 
       {0, 0, 0, 0}  }; 
    while (1) { 
     getopt_ret = getopt_long(argc, argv, "p:b::", 
            long_options, &option_index); 
     if (getopt_ret == -1) break; 

     switch(getopt_ret) 
     { 
      case 0: break; 
      case 'p': 
       printf("Kudos to %s\n", optarg); break; 
      case 'b': 
       printf("You suck "); 
       if (optarg) 
        printf (", %s!\n", optarg); 
       else 
        printf ("!\n", optarg); 
       break; 
      case '?': 
       printf("Unknown option\n"); break; 
     } 
    } 
    return 0; 
} 
+2

Estoy documentando esto aquí con la respuesta, para que otras personas no tengan que golpearse la cabeza contra la pared. – hayalci

Respuesta

76

Aunque no se menciona en la documentación o en la página glibc hombre getopt, argumentos opcionales a los parámetros de línea de comandos estilo largo requieren 'signo igual' (=). El espacio que separa el argumento opcional del parámetro no funciona.

Un ejemplo correr con el código de prueba:

$ ./respond --praise John 
Kudos to John 
$ ./respond --praise=John 
Kudos to John 
$ ./respond --blame John 
You suck ! 
$ ./respond --blame=John 
You suck , John! 
+1

Tenga en cuenta que el módulo Getopt :: Long de Perl NO tiene el mismo requisito. Boost.Program_options hace. –

+9

Guau, esto apesta. Me encontré con el mismo problema con getopt regular() y cuando usaba una cadena optstring "a ::", optarg solo se establecería si tienes CERO espacios entre la opción y el argumento como '-afoo' – SiegeX

+2

Solo encontré esto, pero esto se menciona en el hombre getopt ahora. – abc

1

también se encontró con el mismo problema y vino aquí. Entonces me di cuenta de esto. No tiene mucho caso de uso de "argumento_opcional". Si se requiere una opción, verifique desde la lógica del programa, si una opción es opcional, entonces no necesita hacer nada porque a nivel getopt todas las opciones son opcionales, no son obligatorias, por lo que no hay ningún caso de uso de "argumento opcional". Espero que esto ayude.

ps: para el ejemplo anterior, creo que las opciones correctas son --praise --praise-nombre "nombre" --blame --blame-nombre "nombre"

7

La página de hombre ciertamente no lo hace documentarlo muy bien, pero el código fuente ayuda un poco.

En pocas palabras: se supone que tienes que hacer algo como lo siguiente (aunque esto puede ser un poco demasiado pedante):

if( !optarg 
    && optind < argc // make sure optind is valid 
    && NULL != argv[optind] // make sure it's not a null string 
    && '\0' != argv[optind][0] // ... or an empty string 
    && '-' != argv[optind][0] // ... or another option 
) { 
    // update optind so the next getopt_long invocation skips argv[optind] 
    my_optarg = argv[optind++]; 
} 
/* ... */ 

De entre los comentarios anteriores _getopt_internal:

.. .

Si getopt encuentra otro carácter de opción, lo devuelve, actualizar optind y nextchar para que la próxima llamada al getopt pueda reanudar el escaneo con el siguiente carácter de opción o elemento ARGV.

Si no hay más caracteres de opción, getopt devuelve -1. Entonces optind es el índice en ARGV del primer elemento ARGV que no es una opción. (Los elementos de argv se han permutado para que los que no son opciones ahora vienen pasado.) <-- a note from me: if the 3rd argument to getopt_long starts with a dash, argv will not be permuted

...

Si un caracter en optstring es seguida por dos puntos, lo que significa que quiere un arg , por lo que el siguiente texto en el mismo elemento ARGV, o el texto del siguiente elemento ARGV, se devuelve en optarg.Dos dos puntos significan una opción que quiere una arg opcional; si hay texto en el elemento ARGV actual, se devuelve en optarg, , de lo contrario optarg se establece en cero.

...

... aunque tienes que leer un poco entre líneas. Lo siguiente hace lo que quiere:

#include <stdio.h> 
#include <getopt.h> 

int main(int argc, char* argv[]) { 
    int getopt_ret; 
    int option_index; 
    static struct option long_options[] = { 
     {"praise", required_argument, 0, 'p'} 
    , {"blame", optional_argument, 0, 'b'} 
    , {0, 0, 0, 0} 
    }; 

    while(-1 != (getopt_ret = getopt_long( argc 
              , argv 
              , "p:b::" 
              , long_options 
              , &option_index))) { 
    const char *tmp_optarg = optarg; 
    switch(getopt_ret) { 
     case 0: break; 
     case 1: 
     // handle non-option arguments here if you put a `-` 
     // at the beginning of getopt_long's 3rd argument 
     break; 
     case 'p': 
     printf("Kudos to %s\n", optarg); break; 
     case 'b': 
     if( !optarg 
      && NULL != argv[optindex] 
      && '-' != argv[optindex][0]) { 
      // This is what makes it work; if `optarg` isn't set 
      // and argv[optindex] doesn't look like another option, 
      // then assume it's our parameter and overtly modify optindex 
      // to compensate. 
      // 
      // I'm not terribly fond of how this is done in the getopt 
      // API, but if you look at the man page it documents the 
      // existence of `optarg`, `optindex`, etc, and they're 
      // not marked const -- implying they expect and intend you 
      // to modify them if needed. 
      tmp_optarg = argv[optindex++]; 
     } 
     printf("You suck"); 
     if (tmp_optarg) { 
      printf (", %s!\n", tmp_optarg); 
     } else { 
      printf ("!\n"); 
     } 
     break; 
     case '?': 
     printf("Unknown option\n"); 
     break; 
     default: 
     printf("Unknown: getopt_ret == %d\n", getopt_ret); 
     break; 
    } 
    } 
    return 0; 
} 
+0

Esto funcionó muy bien, gracias. No estoy seguro de dónde obtuviste el optindex; se llama (extern int) optind para mí. – apanloco

+0

Hay un error en el segundo ejemplo de código, debería ser 'optind' en lugar de' optindex'. –

-3

Si escribe el argumento al lado del parámetro sin carácter de espacio tampoco funciona igual. Por ejemplo:

$ ./respond --blameJohn 
You suck John! 
+0

Esto es falso. './respond --blameJohn' ' ./respond: opción no reconocida '--blameJohn'' –