getopt не может обнаружить отсутствующий аргумент для опции - PullRequest
17 голосов
/ 08 февраля 2010

У меня есть программа, которая принимает различные аргументы командной строки. Для упрощения скажем, что для этого требуется 3 флага: -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, а параметрForForB считается опцией без параметра, а для optind устанавливается индекс параметраForB в argv.

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

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

Ответы [ 5 ]

11 голосов
/ 08 февраля 2010

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

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

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

  1. Если параметр был последним символом в строке, на которую указывает элемент argv, тогда optarg должен содержит следующий элемент argv, и optind увеличивается на 2. Если результирующее значение optind больше чем argc, это указывает на отсутствует опция-аргумент и getopt () должен вернуть индикацию ошибки.
  2. В противном случае, 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 ( …
7 голосов
/ 08 февраля 2010

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

4 голосов
/ 08 февраля 2010

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

Будет ли этот пример с 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 ();
    }

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

while((c =  getopt(argc, argv, ":a:b:c")) != EOF)
{
    if (optarg[0] == '-')
    {
        c = ':';
    }
    switch (c)
    {
        ...
    }
}
2 голосов
/ 10 октября 2015

В качестве альтернативы для проектов без Boost, у меня есть простая оболочка C ++ только для заголовков для getopt (по лицензии BSD с 3 пунктами): 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;
}
1 голос
/ 08 февраля 2010

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

...