Подтвердить что ты не робот

Getopt не может обнаружить отсутствующий аргумент для опции

У меня есть программа, которая принимает различные аргументы командной строки. Для упрощения мы скажем, что для анализа моих аргументов используются три флага -a, -b и -c, и используйте следующий код:

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

Примечание: a и b принимают параметры после флага.

Но я столкнулся с проблемой, если я вызываю свою программу с помощью

./myprog -a -b parameterForB

где я забыл параметрForA, параметрForA (представленный optarg) возвращается как -b, а параметрForB считается опцией без параметра, а optind - индексом параметраForB в argv.

Желаемое поведение в этой ситуации будет состоять в том, что ':' возвращается после того, как аргумент -a не найден, и Missing option. печатается со стандартной ошибкой. Однако это происходит только в том случае, если -a является последним параметром, переданным в программу.

Я предполагаю, что вопрос: есть ли способ сделать getopt() предположить, что никакие параметры не начинаются с -?

4b9b3361

Ответ 1

См. стандартное определение POSIX для getopt. В нем говорится, что

Если он [getopt] обнаруживает отсутствующий option-argument, он должен вернуть двоеточие (':'), если первый Характер optstring был двоеточием или символ вопросительного знака ('?') в противном случае.

Что касается этого обнаружения,

  • Если этот параметр был последним символом в строке, на которую указывает элемент argv, то optarg должен содержат следующий элемент argv и optind должен быть увеличен на 2. Если полученное значение optind больше, чем argc, это указывает на отсутствующий параметр-аргумент и getopt() должен возвращать индикацию ошибки.
  • В противном случае optarg указывает на строку после опции символ в этом элементе argv и optind должен быть увеличен на 1.

Похоже, что getopt определяется не для того, чтобы делать то, что вы хотите, поэтому вам нужно выполнить проверку самостоятельно. К счастью, вы можете это сделать, проверив *optarg и изменив 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 ( …

Ответ 2

Если вы работаете в С++, boost:: program_option - моя рекомендация анализировать аргумент командной строки:

Ответ 3

Полное раскрытие: я не эксперт по этому вопросу.

Будет ли этот пример из gnu.org полезен? Кажется, он обрабатывает '?' символ в случаях, когда ожидаемый аргумент не был предоставлен:

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

update: Возможно, следующее будет работать как исправление?

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

Ответ 4

В качестве альтернативы для проектов без Boost у меня есть простая оболочка С++ для заголовков только для getopt (в соответствии с лицензией BSD 3-Clause): https://github.com/songgao/flags.hh

Получено из example.cc в репо:

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

Ответ 5

Существует довольно много разных версий getopt, поэтому, даже если вы можете заставить его работать для одной версии, вероятно, будет по крайней мере пять других, для которых ваш обходной путь будет нарушен. Если у вас нет оснований использовать getopt, я бы рассмотрел что-то еще, например Boost.Program_options.