getopt не анализирует необязательные аргументы для параметров - PullRequest
42 голосов
/ 27 июня 2009

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

Ответы [ 4 ]

83 голосов
/ 27 июня 2009

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

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

$ ./respond --praise John
Kudos to John
$ ./respond --praise=John
Kudos to John
$ ./respond --blame John
You suck !
$ ./respond --blame=John
You suck , John!
15 голосов
/ 15 сентября 2015

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

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

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. Два двоеточия означают вариант, который хочет опциональный аргумент 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'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;
}
1 голос
/ 07 сентября 2015

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

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

0 голосов
/ 16 мая 2017

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

$ ./respond --blameJohn
You suck John!
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...