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

У меня есть программа, которая берет различные параметры командной строки. Ради упрощения мы скажем, что требуется 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

где я забыл parameterForA, parameterForA (представленный optarg) возвращается как -b и parameterForB рассмотрен, возможность без параметра и optind установлена на индекс parameterForB в argv.

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

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

17
задан Potatoswatter 29 December 2016 в 06:04
поделиться

4 ответа

См. определение стандарта 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 ( …
11
ответ дан 30 November 2019 в 13:12
поделиться

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

Будет ли этот пример с 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
ответ дан 30 November 2019 в 13:12
поделиться

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

7
ответ дан 30 November 2019 в 13:12
поделиться

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

1
ответ дан 30 November 2019 в 13:12
поделиться