getopt не анализирует необязательные аргументы для параметров

StackOverflow https://stackoverflow.com/questions/1052746

Вопрос

В C getopt_long не анализирует необязательные аргументы параметров командной строки parameters.

Когда я запускаю программу, необязательный аргумент не распознается, как в примере, приведенном ниже.

$ ./respond --praise John
Kudos to John
$ ./respond --blame John
You suck !
$ ./respond --blame
You suck !

Вот тестовый код.

#include <stdio.h>
#include <getopt.h>

int main(int argc, char ** argv )
{
    int getopt_ret, option_index;
    static struct option long_options[] = {
               {"praise",  required_argument, 0, 'p'},
               {"blame",  optional_argument, 0, 'b'},
               {0, 0, 0, 0}       };
    while (1) {
        getopt_ret = getopt_long( argc, argv, "p:b::",
                                  long_options,  &option_index);
        if (getopt_ret == -1) break;

        switch(getopt_ret)
        {
            case 0: break;
            case 'p':
                printf("Kudos to %s\n", optarg); break;
            case 'b':
                printf("You suck ");
                if (optarg)
                    printf (", %s!\n", optarg);
                else
                    printf ("!\n", optarg);
                break;
            case '?':
                printf("Unknown option\n"); break;
        }
    } 
    return 0;
}
Это было полезно?

Решение

Хотя это не упоминается в документации glibc или справочной странице getopt, необязательные аргументы для параметров командной строки в стиле long требуют 'знака равенства' (=).Пробел, разделяющий необязательный аргумент от параметра, не работает.

Пример запуска с тестовым кодом:

$ ./respond --praise John
Kudos to John
$ ./respond --praise=John
Kudos to John
$ ./respond --blame John
You suck !
$ ./respond --blame=John
You suck , John!

Другие советы

Справочная страница, конечно, не очень хорошо документирует это, но исходный код немного помогает.

Кратко:предполагается, что вы должны сделать что-то вроде следующего (хотя это может быть немного чересчур педантично):

if(   !optarg
   && optind < argc // make sure optind is valid
   && NULL != argv[optind] // make sure it's not a null string
   && '\0' != argv[optind][0] // ... or an empty string
   && '-' != argv[optind][0] // ... or another option
  ) {
  // update optind so the next getopt_long invocation skips argv[optind]
  my_optarg = argv[optind++];
}
/* ... */

Из комментариев , предшествующих _getopt_internal:

...

Если getopt находит другой символ опции, он возвращает этот символ, обновление optind и nextchar так что следующий звонок в getopt может возобновить сканирование с помощью следующего символа опции или ARGV-элемента.

Если дополнительных символов больше нет, getopt возвращает значение -1.Тогда optind является индексом в ARGV первого ARGV-элемента это не вариант.(ARGV-элементы были переставлены так что те, которые не являются вариантами, теперь идут последними.) <-- a note from me: if the 3rd argument to getopt_long starts with a dash, argv will not be permuted

...

Если за символом в OPTSTRING следует двоеточие, это означает, что ему требуется аргумент, таким образом, следующий текст в том же ARGV-элементе или текст следующего ARGV-элемент, возвращаемый в optarg.Два двоеточия означают параметр, который требует необязательного аргумента;если в текущем ARGV-элементе есть текст, он возвращается в optarg, в противном случае optarg установлен на ноль.

...

...хотя вам придется немного почитать между строк.Следующее делает то, что вы хотите:

#include <stdio.h>
#include <getopt.h>

int main(int argc, char* argv[] ) {
  int getopt_ret;
  int option_index;
  static struct option long_options[] = {
      {"praise",  required_argument, 0, 'p'}
    , {"blame",  optional_argument, 0, 'b'}
    , {0, 0, 0, 0}
  };

  while( -1 != ( getopt_ret = getopt_long(  argc
                                          , argv
                                          , "p:b::"
                                          , long_options
                                          , &option_index) ) ) {
    const char *tmp_optarg = optarg;
    switch( getopt_ret ) {
      case 0: break;
      case 1:
        // handle non-option arguments here if you put a `-`
        // at the beginning of getopt_long's 3rd argument
        break;
      case 'p':
        printf("Kudos to %s\n", optarg); break;
      case 'b':
        if(   !optarg
           && NULL != argv[optindex]
           && '-' != argv[optindex][0] ) {
          // This is what makes it work; if `optarg` isn't set
          // and argv[optindex] doesn't look like another option,
          // then assume it's our parameter and overtly modify optindex
          // to compensate.
          //
          // I'm not terribly fond of how this is done in the getopt
          // API, but if you look at the man page it documents the
          // existence of `optarg`, `optindex`, etc, and they're
          // not marked const -- implying they expect and intend you
          // to modify them if needed.
          tmp_optarg = argv[optindex++];
        }
        printf( "You suck" );
        if (tmp_optarg) {
          printf (", %s!\n", tmp_optarg);
        } else {
          printf ("!\n");
        }
        break;
      case '?':
        printf("Unknown option\n");
        break;
      default:
        printf( "Unknown: getopt_ret == %d\n", getopt_ret );
        break;
    }
  }
  return 0;
}

Я тоже столкнулся с такой же проблемой и пришел сюда.Тогда я понял это .У вас не так много вариантов использования "optional_argument" .Если опция обязательна, вы проверяете из логики программы, если опция необязательна, то вам не нужно ничего делать, потому что на уровне getopt все опции необязательны, они не являются обязательными, поэтому "optional_argument" не используется.Надеюсь, это поможет.

ps:для приведенного выше примера, я думаю, правильными вариантами являются --похвала -praise-name "имя" -blame -blame-name "имя"

Если вы пишете аргумент рядом с параметром без пробела, то ни одно из значений equal также не работает.Например:

$ ./respond --blameJohn
You suck John!
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top