2009-06-27 4 views
37

En C, getopt_long n'analyse pas les arguments facultatifs en paramètres de paramètres de ligne de commande.getopt n'analyse pas les arguments facultatifs en paramètres

Quand je lance le programme, l'argument optionnel n'est pas reconnu comme l'exemple ci-dessous courir.

$ ./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; 
} 
+2

Je documente ceci ici avec la réponse, ainsi d'autres personnes n'ont pas besoin de se frapper la tête contre le mur. – hayalci

Répondre

76

Bien que non mentionné dans la documentation de la glibc ou page man getopt, arguments facultatifs se rapportant à des paramètres de ligne de commande longue de style require 'signe égal' (=). L'espace séparant l'argument facultatif du paramètre ne fonctionne pas.

Un exemple courir 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! 
+1

Notez que le module Getopt :: Long de Perl n'a PAS la même exigence. Boost.Program_options fait. –

+9

Wow, ça craint. J'ai rencontré le même problème avec getopt() régulier et quand j'utilise un optstring "a ::", optarg ne serait défini que si vous avez ZERO espace entre l'option et l'argument comme '-afoo' – SiegeX

+2

ceci est mentionné dans l'homme getopt maintenant. – abc

1

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

ps: pour l'exemple ci-dessus, je pense que les bonnes options sont --praise --praise-name "name" --blame --blame-name "nom"

7

La page de manuel ne signifie certainement pas documentez-le 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 peut être un peu trop pédant):

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édents _getopt_internal:

.. .

Si getopt trouve un autre caractère de l'option, il retourne ce caractère, mise à jour optind et nextchar afin que l'appel suivant à getopt peut reprendre l'analyse avec le caractère d'option ou l'élément ARGV suivant. S'il n'y a plus de caractères d'option, getopt renvoie -1. Alors optind est l'index en ARGV du premier élément ARGV qui n'est pas une option. (Les éléments argv ont été permuté afin que ceux qui ne sont pas les options sont désormais disponibles dernière.) <-- a note from me: if the 3rd argument to getopt_long starts with a dash, argv will not be permuted

...

Si un caractère dans OPTSTRING est suivi par deux points, ce qui signifie qu'il veut un arg , de sorte que le texte suivant dans le même élément ARGV, ou le texte de l'élément ARGV suivant, soit renvoyé dans optarg.Deux deux-points signifient une option que veut un argument optionnel; S'il y a du texte dans l'élément ARGV en cours, , il est renvoyé sous optarg, sinon optarg est défini sur.

...

... 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; 
} 
+0

Cela a très bien fonctionné, merci. Je ne sais pas d'où vous avez choisi optindex; ça s'appelle (extern int) optind pour moi. – apanloco

+0

Il y a une erreur dans le second exemple de code, il devrait être 'optind' au lieu de' optindex'. –

-3

Si vous écrivez l'argument à côté du paramètre sans caractère espace, aucun des deux ne fonctionne également. Par exemple:

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

Ceci est faux. './respond --blameJohn' ' ./respond: option non reconnue '--blameJohn'' –

Questions connexes