web-dev-qa-db-fra.com

getOpt n'utilise pas d'arguments facultatifs aux paramètres

En C, getopt_long n'utilise pas les arguments facultatifs aux paramètres de paramètres de ligne de commande.

Lorsque j'exécute le programme, l'argument facultatif n'est pas reconnu comme l'exemple exécuté ci-dessous.

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

Voici le code de test.

#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;
}
43
hayalci

Bien que non mentionné dans la page Documentation Glibc ou GetOpT Man, les arguments facultatifs des paramètres de ligne de commande de style longue nécessitent des paramètres "Signal" (=). L'espace séparant l'argument facultatif du paramètre ne fonctionne pas.

Un exemple exécuté avec le code de test:

$ ./respond --praise John
Kudos to John
$ ./respond --praise=John
Kudos to John
$ ./respond --blame John
You suck !
$ ./respond --blame=John
You suck , John!
87
hayalci

La page Homme ne le documente certainement pas très bien, mais le code source aide un peu.

En bref: vous êtes censé faire quelque chose comme ce qui suit (bien que cela puisse être un peu surnommable):

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++];
}
/* ... */

Parmi les commentaires précédant _getopt_internal:

...

Si getopt trouve un autre caractère d'option, il renvoie ce caractère, Mise à jour optind et nextchar _ afin que le prochain appel à getopt Peut reprendre le balayage avec le caractère d'option suivant ou l'élément argv.

S'il n'y a plus de caractères d'option, getopt renvoie -1. Alors optind est l'index dans ARGV du premier élément argv qui n'est pas une option. (Les éléments argv-éléments ont été permutés de manière à ce que ceux qui ne soient pas options viennent maintenant.) <-- a note from me: if the 3rd argument to getopt_long starts with a dash, argv will not be permuted

...

Si un caractère d'optstring est suivi d'un point de côlon, cela signifie qu'il veut une ARG, le texte suivant dans le même élément argv, ou le texte de l'élément argv-élément suivant est renvoyé dans optarg. Deux colons signifient une option qui veut un argument facultatif; S'il y a du texte dans l'élément argv actuel, il est renvoyé dans optarg, sinon optarg est défini sur zéro.

...

... si vous devez faire une lecture entre les lignes. Ce qui suit fait ce que vous voulez:

#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;
}
15
Brian Vandenberg

J'ai aussi couru dans le même problème et je suis venu ici. Ensuite, j'ai réalisé cela. Vous n'avez pas beaucoup d'un cas d'utilisation de "option_argument". Si une option est requise, vérifiez à partir de la logique du programme, si une option est facultative, vous ne devez rien faire car au niveau GetOpt Toutes les options sont facultatives, elles ne sont pas obligatoires, il n'y a donc pas d'utilisation de "option_argument". J'espère que cela t'aides.

pS: Pour l'exemple ci-dessus, je pense que les options correctes sont --prise - préchaise-nom "nom" --Beame --Bame-Name "Nom"

1
user5081924