2009-06-27 9 views
37

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

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

$ ./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; 
} 
+2

Я документирую это здесь с ответом, поэтому другим людям не нужно ударять головой о стену. – hayalci

ответ

76

Хотя не упоминается в 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! 
+1

Обратите внимание, что модуль Getopt :: Long Perl не имеет того же требования. Boost.Program_options делает. –

+9

Ничего себе, это отстой. Я столкнулся с той же проблемой с регулярным getopt(), и при использовании optstring «a ::», optarg будет установлен только в том случае, если у вас есть ZERO-пространство между опцией и аргументом, например «-afoo» – SiegeX

+2

Просто наткнулся на это, но это уже упоминается в man getopt. – abc

1

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

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

7

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

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

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

...

Если символ в строка_опциях следует двоеточие, что означает, что он хочет ARG , , поэтому следующий текст в том же элементе ARGV или текст следующего ARGV-element, возвращается в 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; 
} 
+0

Это сработало очень хорошо, спасибо. Не знаете, откуда у вас есть optindex; он называется (extern int) optind для меня. – apanloco

+0

Во втором примере кода есть ошибка, она должна быть 'optind' вместо' optindex'. –

-3

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

+0

Это неверно. './ответ --blameJohn' ' ./respond: непризнанная опция '--blameJohn'' –