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

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

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

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

$ ./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;
}
4b9b3361

Ответ 1

Несмотря на то, что в документации glibc или странице getopt man не упоминается, необязательные аргументы для параметров командной строки с длинным стилем требуют "equals sign" (=). Пробел, отделяющий необязательный аргумент от параметра, не работает.

Пример с тестовым кодом:

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

Ответ 2

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

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

if(   !optarg
   && optind < argc // make sure optind is valid
   && NULL != argv[optind] // make sure it 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

...

Если a char в OPTSTRING сопровождается двоеточием, это означает, что ему нужен arg, поэтому следующий текст в том же ARGV-элементе или текст следующего ARGV-элемент возвращается в optarg. Два двоеточия означают вариант, который хочет необязательный аргумент arg; если в текущем 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 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 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;
}

Ответ 3

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

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

Ответ 4

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

$ ./respond --blameJohn
You suck John!