web-dev-qa-db-fra.com

getOpt ne parvient pas à détecter l'argument manquant pour l'option

J'ai un programme qui prend divers arguments de ligne de commande. Pour des raisons de simplification, nous dirons qu'il faut 3 indicateurs, -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;
        }
    }

remarque: A, et B prenez des paramètres après le drapeau.

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

./myprog -a -b parameterForB

lorsque j'ai oublié Parameterfora, le paramètrefora (représenté par Optarg) est renvoyé comme -b et le paramètreforb est considéré comme une option sans paramètre et OptinD est défini sur l'index de paramètreforb dans ARGV.

Le comportement souhaité dans cette situation serait que ':' Est renvoyé après que aucun argument ne soit trouvé pour -a, Et Missing option. Est imprimé à une erreur standard. Cependant, cela ne se produit que dans le cas où -a Est le dernier paramètre passé dans le programme.

Je suppose que la question est la suivante: y a-t-il un moyen de faire getopt() suppose qu'aucune option ne commencera par -?

17
finiteloop

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

Si elle [getopt] détecte un argument d'option manquant, il retournera le caractère du côlon (':') Si le premier caractère d'optstring était un côlon, ou un caractère de marque de questions ('?') Sinon.

Quant à cette détection,

  1. Si l'option était le dernier caractère de la chaîne indiquée par un élément de ARGV, Optarg doit contenir l'élément suivant de ARGV, et OptinD doit être incrémenté par 2. Si la valeur résultante de l'OptinD est supérieure à celle de l'OptinD, cela indique un L'argument d'option manquante et GetOPT () renvoient une indication d'erreur.
  2. Sinon, Optarg doit pointer sur la chaîne suivant le caractère d'option dans cet élément de ARGV et OptinD doit être incrémenté de 1.

On dirait que getopt est défini pour ne pas faire ce que vous voulez, vous devez donc mettre en œuvre le chèque vous-même. Heureusement, vous pouvez le faire en inspectant *optarg et changer optind vous-même.

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

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

7
Phong

Divulgation complète: je ne suis pas un expert à ce sujet.

Serait Cet exemple de gnu.org sera 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 que ce qui suit fonctionnerait comme un correctif?

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

Comme alternative pour des projets sans plus bascules, j'ai un wrapper C++ simple simple pour getopt (sous la licence BSD 3-Clause): https://github.com/songgao/flags .hh

Pris à partir 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;
}
2
Song Gao

Il y a plusieurs versions différentes de getopt autour, donc même si vous pouvez le faire fonctionner pour une version, il y aura probablement au moins cinq autres pour lesquels votre contournement se cassera. Sauf si vous avez une raison écrasante d'utiliser GetOPT, je considérerais quelque chose d'autre, tel que boost.program_option .

1
Jerry Coffin