可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a program which takes various command line arguments. For the sake of simplification, we will say it takes 3 flags, -a
, -b
, and -c
, and use the following code to parse my arguments:
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;
}
}
note: a, and b take parameters after the flag.
But I run into an issue if I invoke my program say with
./myprog -a -b parameterForB
where I forgot parameterForA, the parameterForA (represented by optarg) is returned as -b
and parameterForB is considered an option with no parameter and optind is set to the index of parameterForB in argv.
The desired behavior in this situation would be that ':'
is returned after no argument is found for -a
, and Missing option.
is printed to standard error. However, that only occurs in the event that -a
is the last parameter passed into the program.
I guess the question is: is there a way to make getopt()
assume that no options will begin with -
?
回答1:
See the POSIX standard definition for getopt
. It says that
If it [getopt] detects a missing
option-argument, it shall return the
colon character ( ':' ) if the first
character of optstring was a colon, or
a question-mark character ( '?' )
otherwise.
As for that detection,
- If the option was the last character in the string pointed to by
an element of argv, then optarg shall
contain the next element of argv, and
optind shall be incremented by 2. If
the resulting value of optind is
greater than argc, this indicates a
missing option-argument, and getopt()
shall return an error indication.
- Otherwise, optarg shall point to the string following the option
character in that element of argv, and
optind shall be incremented by 1.
It looks like getopt
is defined not to do what you want, so you have to implement the check yourself. Fortunately, you can do that by inspecting *optarg
and changing optind
yourself.
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:
If you are working in C++, boost::program_option is my recommendation to parse command line argument:
- Boost::program_options library
回答3:
Full disclosure: I'm no expert on this matter.
Would this example from gnu.org be of use? It seems to handle the '?' character in cases where an expected argument was not supplied:
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: Perhaps the following would work as a fix?
while((c = getopt(argc, argv, ":a:b:c")) != EOF)
{
if (optarg[0] == '-')
{
c = ':';
}
switch (c)
{
...
}
}
回答4:
As an alternative for Boost-free projects, I have a simple header-only C++ wrapper for getopt
(under The BSD 3-Clause License): https://github.com/songgao/flags.hh
Taken from example.cc
in the repo:
#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:
There are quite a few different versions of getopt
around, so even if you can get it to work for one version, there will probably be at least five others for which your workaround will break. Unless you have an overwhelming reason to use getopt, I'd consider something else, such as Boost.Program_options.