Question

J'ai un programme qui prend plusieurs arguments de ligne de commande. Par souci de simplification, nous dirons qu'il faut 3 drapeaux, -a, -b et -c et utilisez le code suivant pour analyser mes arguments:

    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;
        }
    }

Note:. A et b les paramètres de prise après le drapeau

Mais je rencontre un problème si j'invoque mon programme dire avec

./myprog -a -b parameterForB

où oublié parameterForA, le parameterForA (représenté par optarg) est renvoyée comme -b et parameterForB est considéré comme une option sans paramètre et optind est réglé sur l'indice de parameterForB dans argv.

Le comportement souhaité dans cette situation serait que ':' est retourné après aucun argument est trouvé pour -a et Missing option. est imprimé à l'erreur standard. Cependant, se produit que seulement dans le cas où -a est le dernier paramètre passé dans le programme.

Je suppose que la question est: est-il un moyen de faire getopt() supposer que aucune option ne commence par -

?
Était-ce utile?

La solution

Voir la définition standard POSIX pour getopt. Il dit que

  

Si elle [getopt] détecte un manque   Option argument, il renvoie le   caractère deux points ( « : ») si le premier   caractère de optstring était un colon, ou   un caractère point d'interrogation (?)   autrement.

En ce qui concerne cette détection,

  
      
  1. Si l'option était le dernier caractère de la chaîne pointée par   un élément de argv, OPTARG puis doit   contenir l'élément suivant de argv, et   optind est incrémentée par 2. Si   la valeur résultante de optind est   supérieure à argc, cela indique un   manque l'option argument, et getopt ()   doit renvoyer une indication d'erreur.
  2.   
  3. Dans le cas contraire, optarg doit pointer vers la chaîne suivante l'option   caractère en ce que l'élément de argv, et   optind sont majorés de 1.
  4.   

Il semble que getopt est défini de ne pas faire ce que vous voulez, alors vous devez mettre en œuvre le vérifier vous-même. Heureusement, vous pouvez le faire en inspectant *optarg et changer vous optind.

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 ( …

Autres conseils

Si vous travaillez en C ++, boost :: program_option est ma recommandation pour analyser l'argument de ligne de commande:

La divulgation complète:. Je ne suis pas expert en la matière

cet exemple de gnu .org être utile? Il semble gérer le « ? » caractère dans les cas où un argument attendu n'a pas été fourni:

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 ();
    }

Mise à jour: Peut-être ce qui suit fonctionnerait comme une solution

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

Comme alternative pour des projets sans Boost, j'ai un en-tête seulement simple wrapper C ++ pour getopt (sous la licence BSD 3-article): https://github.com/songgao/flags.hh

Pris de example.cc dans le repo:

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

Il y a tout à fait quelques versions différentes de getopt autour, même si vous pouvez l'obtenir à travailler pour une version, il y aura probablement au moins cinq autres pour lesquels votre solution de contournement briser. Sauf si vous avez une raison écrasante d'utiliser getopt, je considérerais autre chose, comme Boost.Program_options .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top