Getopt не может обнаружить отсутствующий аргумент для опции
У меня есть программа, которая принимает различные аргументы командной строки. Для упрощения мы скажем, что для анализа моих аргументов используются три флага -a
, -b
и -c
, и используйте следующий код:
int c;
while((c = getopt(argc, argv, ":a:b:c")) != EOF)
{
switch (c)
{
case 'a':
cout << optarg << endl;
break;
case 'b':
cout << optarg << endl;
break;
case ':':
cerr << "Missing option." << endl;
exit(1);
break;
}
}
Примечание: a и b принимают параметры после флага.
Но я столкнулся с проблемой, если я вызываю свою программу с помощью
./myprog -a -b parameterForB
где я забыл параметрForA, параметрForA (представленный optarg) возвращается как -b
, а параметрForB считается опцией без параметра, а optind - индексом параметраForB в argv.
Желаемое поведение в этой ситуации будет состоять в том, что ':'
возвращается после того, как аргумент -a
не найден, и Missing option.
печатается со стандартной ошибкой. Однако это происходит только в том случае, если -a
является последним параметром, переданным в программу.
Я предполагаю, что вопрос: есть ли способ сделать getopt()
предположить, что никакие параметры не начинаются с -
?
Ответы
Ответ 1
См. стандартное определение POSIX для getopt
. В нем говорится, что
Если он [getopt] обнаруживает отсутствующий option-argument, он должен вернуть двоеточие (':'), если первый Характер optstring был двоеточием или символ вопросительного знака ('?') в противном случае.
Что касается этого обнаружения,
- Если этот параметр был последним символом в строке, на которую указывает элемент argv, то optarg должен содержат следующий элемент argv и optind должен быть увеличен на 2. Если полученное значение optind больше, чем argc, это указывает на отсутствующий параметр-аргумент и getopt() должен возвращать индикацию ошибки.
- В противном случае optarg указывает на строку после опции символ в этом элементе argv и optind должен быть увеличен на 1.
Похоже, что getopt
определяется не для того, чтобы делать то, что вы хотите, поэтому вам нужно выполнить проверку самостоятельно. К счастью, вы можете это сделать, проверив *optarg
и изменив optind
самостоятельно.
int c, prev_ind;
while(prev_ind = optind, (c = getopt(argc, argv, ":a:b:c")) != EOF)
{
if ( optind == prev_ind + 2 && *optarg == '-' ) {
c = ':';
-- optind;
}
switch ( …
Ответ 2
Если вы работаете в С++, boost:: program_option - моя рекомендация анализировать аргумент командной строки:
Ответ 3
Полное раскрытие: я не эксперт по этому вопросу.
Будет ли этот пример из gnu.org полезен? Кажется, он обрабатывает '?' символ в случаях, когда ожидаемый аргумент не был предоставлен:
while ((c = getopt (argc, argv, "abc:")) != -1)
switch (c)
{
case 'a':
aflag = 1;
break;
case 'b':
bflag = 1;
break;
case 'c':
cvalue = optarg;
break;
case '?':
if (optopt == 'c')
fprintf (stderr, "Option -%c requires an argument.\n", optopt);
else if (isprint (optopt))
fprintf (stderr, "Unknown option `-%c'.\n", optopt);
else
fprintf (stderr,
"Unknown option character `\\x%x'.\n",
optopt);
return 1;
default:
abort ();
}
update: Возможно, следующее будет работать как исправление?
while((c = getopt(argc, argv, ":a:b:c")) != EOF)
{
if (optarg[0] == '-')
{
c = ':';
}
switch (c)
{
...
}
}
Ответ 4
В качестве альтернативы для проектов без Boost у меня есть простая оболочка С++ для заголовков только для getopt
(в соответствии с лицензией BSD 3-Clause): https://github.com/songgao/flags.hh
Получено из example.cc
в репо:
#include "Flags.hh"
#include <cstdint>
#include <iostream>
int main(int argc, char ** argv) {
uint64_t var1;
uint32_t var2;
int32_t var3;
std::string str;
bool b, help;
Flags flags;
flags.Var(var1, 'a', "var1", uint64_t(64), "This is var1!");
flags.Var(var2, 'b', "var2", uint32_t(32), "var2 haahahahaha...");
flags.Var(var3, 'c', "var3", int32_t(42), "var3 is signed!", "Group 1");
flags.Var(str, 's', "str", std::string("Hello!"), "This is a string, and the description is too long to fit in one line and has to be wrapped blah blah blah blah...", "Group 1");
flags.Bool(b, 'd', "bool", "this is a bool variable", "Group 2");
flags.Bool(help, 'h', "help", "show this help and exit", "Group 3");
if (!flags.Parse(argc, argv)) {
flags.PrintHelp(argv[0]);
return 1;
} else if (help) {
flags.PrintHelp(argv[0]);
return 0;
}
std::cout << "var1: " << var1 << std::endl;
std::cout << "var2: " << var2 << std::endl;
std::cout << "var3: " << var3 << std::endl;
std::cout << "str: " << str << std::endl;
std::cout << "b: " << (b ? "set" : "unset") << std::endl;
return 0;
}
Ответ 5
Существует довольно много разных версий getopt
, поэтому, даже если вы можете заставить его работать для одной версии, вероятно, будет по крайней мере пять других, для которых ваш обходной путь будет нарушен. Если у вас нет оснований использовать getopt, я бы рассмотрел что-то еще, например Boost.Program_options.