The man page certainly doesn’t document it very well, but the source code helps a little.
Briefly: you’re supposed to do something like the following (though this may be a little over-pedantic):
if( !optarg
&& optind < argc // make sure optind is valid
&& NULL != argv[optind] // make sure it's not a null string
&& '' != 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++];
}
/* ... */
From among the comments preceding _getopt_internal:
…
If
getopt
finds another option character, it returns that character,
updatingoptind
andnextchar
so that the next call togetopt
can
resume the scan with the following option character or ARGV-element.If there are no more option characters,
getopt
returns -1.
Thenoptind
is the index in ARGV of the first ARGV-element
that is not an option. (The ARGV-elements have been permuted
so that those that are not options now come last.)<-- a note from me:
if the 3rd argument to getopt_long starts with a dash, argv will not
be permuted…
If a char in OPTSTRING is followed by a colon, that means it wants an arg,
so the following text in the same ARGV-element, or the text of the following
ARGV-element, is returned inoptarg
. Two colons mean an option that
wants an optional arg; if there is text in the current ARGV-element,
it is returned inoptarg
, otherwiseoptarg
is set to zero.…
… though you have to do some reading between the lines. The following does what you want:
#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 %sn", optarg); break;
case 'b':
if( !optarg
&& NULL != argv[optind]
&& '-' != argv[optind][0] ) {
// This is what makes it work; if `optarg` isn't set
// and argv[optind] doesn't look like another option,
// then assume it's our parameter and overtly modify optind
// 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`, `optind`, etc, and they're
// not marked const -- implying they expect and intend you
// to modify them if needed.
tmp_optarg = argv[optind++];
}
printf( "You suck" );
if (tmp_optarg) {
printf (", %s!n", tmp_optarg);
} else {
printf ("!n");
}
break;
case '?':
printf("Unknown optionn");
break;
default:
printf( "Unknown: getopt_ret == %dn", getopt_ret );
break;
}
}
return 0;
}
The man page certainly doesn’t document it very well, but the source code helps a little.
Briefly: you’re supposed to do something like the following (though this may be a little over-pedantic):
if( !optarg
&& optind < argc // make sure optind is valid
&& NULL != argv[optind] // make sure it's not a null string
&& '' != 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++];
}
/* ... */
From among the comments preceding _getopt_internal:
…
If
getopt
finds another option character, it returns that character,
updatingoptind
andnextchar
so that the next call togetopt
can
resume the scan with the following option character or ARGV-element.If there are no more option characters,
getopt
returns -1.
Thenoptind
is the index in ARGV of the first ARGV-element
that is not an option. (The ARGV-elements have been permuted
so that those that are not options now come last.)<-- a note from me:
if the 3rd argument to getopt_long starts with a dash, argv will not
be permuted…
If a char in OPTSTRING is followed by a colon, that means it wants an arg,
so the following text in the same ARGV-element, or the text of the following
ARGV-element, is returned inoptarg
. Two colons mean an option that
wants an optional arg; if there is text in the current ARGV-element,
it is returned inoptarg
, otherwiseoptarg
is set to zero.…
… though you have to do some reading between the lines. The following does what you want:
#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 %sn", optarg); break;
case 'b':
if( !optarg
&& NULL != argv[optind]
&& '-' != argv[optind][0] ) {
// This is what makes it work; if `optarg` isn't set
// and argv[optind] doesn't look like another option,
// then assume it's our parameter and overtly modify optind
// 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`, `optind`, etc, and they're
// not marked const -- implying they expect and intend you
// to modify them if needed.
tmp_optarg = argv[optind++];
}
printf( "You suck" );
if (tmp_optarg) {
printf (", %s!n", tmp_optarg);
} else {
printf ("!n");
}
break;
case '?':
printf("Unknown optionn");
break;
default:
printf( "Unknown: getopt_ret == %dn", getopt_ret );
break;
}
}
return 0;
}
у меня есть программа, которая принимает различные аргументы командной строки. Для упрощения мы скажем, что требуется 3 флага,-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
где я забыл parameterForA, parameterForA (представленный optarg) возвращается как -b
и parameterForB считается опцией без параметра, а optind устанавливается в индекс parameterForB в argv.
желаемым поведением в этой ситуации было бы то, что ':'
возвращается после того, как аргумент не найден для -a
и Missing option.
выводится в стандартный поток ошибок. Однако это происходит только в том случае,-a
— последний параметр, переданный в программу.
я думаю, вопрос в том, есть ли способ сделать getopt()
предположим, что никакие параметры не будут начинаются с -
?
5 ответов
посмотреть стандартное определение POSIX на getopt
. Он говорит, что
если он [getopt] обнаруживает отсутствие
option-аргумент, он должен вернуть
символ двоеточия ( ‘:’ ) если первый
символ 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 ( …
полное раскрытие: я не эксперт по этому вопросу.
б от 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 ();
}
обновление: возможно, следующее будет работать как исправить?
while((c = getopt(argc, argv, ":a:b:c")) != EOF)
{
if (optarg[0] == '-')
{
c = ':';
}
switch (c)
{
...
}
}
в качестве альтернативы для проектов без наддува у меня есть простая оболочка только для заголовка C++ для 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;
}
существует довольно много различных версий getopt
вокруг, поэтому, даже если вы можете заставить его работать для одной версии, вероятно, будет по крайней мере пять других, для которых ваш обходной путь сломается. Если у вас нет подавляющей причины использовать getopt, я бы рассмотрел что-то еще, например импульс.Program_options.