getopt не анализирует необязательные аргументы для параметров
-
20-08-2019 - |
Вопрос
В 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!