[英]Why does C getopt_long_only() not set optopt for unknown option?
I am trying to use getopt_long_only()
with custom error messages. 我试图使用带有自定义错误消息的
getopt_long_only()
。 The code is shown below. 代码如下所示。 I tried setting opterr=0 and using a colon at the start of the optstring to disable built-in error messages.
我尝试设置opterr = 0并在optstring的开头使用冒号来禁用内置错误消息。 I added a block of code controlled with boolean
optoptWorks = true
to try to customize error messages, for example to print a message when a bad option like -z
is used. 我添加了一个用boolean
optoptWorks = true
控制的代码块来尝试自定义错误消息,例如在使用-z
这样的错误选项时打印消息。 However optopt
is always set to 0 and the ?
但是
optopt
总是设置为0而且?
error message that I am using does not make sense. 我正在使用的错误消息没有意义。 The ':' (colon case) errors (missing argument such as
-d
) does work OK for custom messages. ':'(冒号)错误(缺少参数,如
-d
)对自定义消息确实有效。 Turning built-in error messages off and handling in ?
关闭内置错误信息并处理
?
seems to result in optopt
always being set to 0 so I can't print the offending option ( -z is not recognized
). 似乎导致
optopt
始终设置为0,因此我无法打印违规选项( -z is not recognized
)。 I compiled on Debian Linux gcc 4.9.4 and also Cygwin gcc 7.3.0 and both give the same result. 我在Debian Linux gcc 4.9.4以及Cygwin gcc 7.3.0上编译,两者都给出了相同的结果。 It seems like
getopt_long_only()
may not set optopt
properly or am I missing something? 似乎
getopt_long_only()
可能没有正确设置optopt
或者我错过了什么? Many examples on the web get around this by either using the built-in error messages or just printing usage without telling the user which option is not recognized. Web上的许多示例通过使用内置错误消息或仅打印用法而不告诉用户哪个选项无法识别来解决这个问题。
Here is output with optoptWorks=false
: 这是
optoptWorks=false
输出:
$ ./testoptget -z
testoptget: unknown option -- z
-d # Set the debug level.
-h, --help Print program usage.
-q Run in quiet mode (log messages to syslog but not console).
-v, --version Print program version.
$ ./testoptget -d
testoptget: option requires an argument -- d
-d # Set the debug level.
-h, --help Print program usage.
-q Run in quiet mode (log messages to syslog but not console).
-v, --version Print program version.
and here is output with optoptWorks=true
: 这是输出
optoptWorks=true
:
$ ./testoptget -z
[ERROR] Unknown option character '\x0'.
-d # Set the debug level.
-h, --help Print program usage.
-q Run in quiet mode (log messages to syslog but not console).
-v, --version Print program version.
$ ./testoptget -d
[ERROR] Option '-d' is missing argument.
-d # Set the debug level.
-h, --help Print program usage.
-q Run in quiet mode (log messages to syslog but not console).
-v, --version Print program version.
The code follows: 代码如下:
/*
Test program for getopt_long_only
*/
#include <ctype.h>
#include <getopt.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int debug = 0; /* Default to not debugging */
/**
Print the program usage.
*/
void usage(void)
{
/* Print the program name and version */
printf("\n");
printf("-d # Set the debug level.\n");
printf("-h, --help Print program usage.\n");
printf("-q Run in quiet mode (log messages to syslog but not console).\n");
printf("-v, --version Print program version.\n\n");
exit(0);
}
/**
Parse command line parameters and set data for program.
@param argc number of command line parameters
@param argv list of command line parameters
*/
void parseargs(int argc,char **argv)
{ /*
See: https://www.gnu.org/software/libc/manual/html_node/Getopt.html#Getopt
See: http://man7.org/linux/man-pages/man3/getopt.3.html
Because legacy -version and --version need to be supported, use getopts_long_only.
*/
/*
The meaning of the following is:
name - the name of the long option
has_arg - whether the option has an argument like --arg param or --arg=param
flag - the numeric value to return (set to "opt" below), if NULL or zero, return "val"
val - the value to return (set to "opt" below) if "flag" not set, use the one-character equivalent
*/
static struct option long_options[] = {
{ "help", no_argument, 0, 'h' }, /* returns as if -v, index not needed */
{ "version", no_argument, 0, 'v' }, /* returns as if -h, index not needed */
{ 0, 0, 0, 0 } /* last element of array must be zeros */
};
int long_index = 0;
int opt;
int errorCount = 0;
/* In <unistd.h>: external int optind, opterr, optopt */
bool optoptWorks = false; /* Apparently optopt gets set to 0 for unknown argument so let the getopt_long_only print the error */
char optstring[32] = "d:hqv";
if ( optoptWorks ) {
/*
If getopt_long_only works as it is supposed to...
Set opterr to zero so getopt calls won't print an error - check for errors in '?' return value
Also use : as first character of optstring to cause : to be used for error handling
*/
opterr = 0;
/* Do the following because strcat is not safe on overlapping strings */
char optstring2[32];
strcpy(optstring2,optstring);
strcpy(optstring,":");
strcat(optstring,optstring2);
}
while((opt = getopt_long_only(argc, argv, optstring, long_options, &long_index)) != -1) {
switch (opt) { /* Will match single character option or long_options val or flag */
case 'd':
/* -d #, Set the debug level to the argument value */
debug = atoi(optarg);
break;
case 'h':
/*
-h, print the usage and exit
-help
--help
*/
usage();
exit(0);
break;
case 'q':
/* -q, indicate that messages should not be printed to stdout */
break;
case 'v':
/*
-v, print the version via standard function,
-version
--version
*/
break;
case ':':
/*
This is an error indicator indicated by : at the start of get_opt_long 3rd argument.
Handle missing argument, such as -d but no argument.
*/
fprintf(stderr, "[ERROR] Option '-%c' is missing argument.\n", optopt);
++errorCount;
break;
case '?':
/*
Handle unknown parameters as per getopt man page example.
"optopt" should contain the offending argument, but perhaps matches last long argument (zero record).
Note that legacy ? command line parameter is no longer supported.
*/
if (isprint(optopt)) {
/* Printable character so print it in the warning. */
if ( optoptWorks ) {
fprintf(stderr, "[ERROR] Unknown option '-%c'.\n", optopt);
}
++errorCount;
}
else {
/* Nonprintable character so show escape sequence. */
if ( optoptWorks ) {
fprintf(stderr, "[ERROR] Unknown option character '\\x%x'.\n", optopt);
}
++errorCount;
}
break;
} /* end switch */
} /* end while */
if ( errorCount > 0 ) {
usage();
exit(1);
}
}
/**
Main program.
@param argc number of command line parameters
@param argv list of command line parameters
@param arge list of environment variables
*/
int main(int argc,char **argv,char **arge)
{
/* Parse command arguments */
parseargs(argc,argv);
/* Normal program termination */
return(0);
}
optopt
is indeed set to zero when unknown long option is found, see here . 当找到未知的长选项时,
optopt
确实设置为零,请参见此处 。 However seems to me you can use optind - 1
as the index in argv
to print the offending option, as optind
is incremented here right before getopt
returns '?'
但是在我看来你可以使用
optind - 1
作为argv
的索引来打印违规选项,因为在getopt
返回'?'
之前, optind
会在此处递增'?'
. 。
As far as I understood, your goal is to just specify custom error messages. 据我所知,您的目标是只指定自定义错误消息。
Also from man getopt_long: 另外来自man getopt_long:
If the first character (following any optional '+' or '-' described above) of optstring is a colon (':'), then getopt() likewise does not print an error message.
如果optstring的第一个字符(在上面描述的任何'+'或' - '之后)是冒号(':'),那么getopt()同样不会打印错误消息。 In addition, it returns ':' instead of '?'
另外,它返回':'而不是'?' to indicate a missing option argument.
表示缺少选项参数。 This allows the caller to distinguish the two different types of errors.
这允许调用者区分两种不同类型的错误。
The documentation you referenced is about getopt
not about getopt_long_only
. 您引用的文档是关于
getopt
而不是getopt_long_only
。 The man getopt_long_only indeed says that getopt_long() function works like getopt()
but the optopt
is set to the "option character". 男人getopt_long_only确实说
getopt_long() function works like getopt()
但是optopt
被设置为“选项字符”。 In case of long options there is no "option character" but an "option string" (as I would call it) - seems to me logical to set optopt
as zero. 在长期期权的情况下,没有“期权字符”,但是“选项字符串”(正如我所说的那样) - 似乎合乎逻辑地将
optopt
设置为零。
So depending on the initial character in optstring, the :
or ?
所以取决于optstring中的初始字符,
:
或?
is returned, as implemented here and here and here . 返回,在此处以及此处和此处实现 。
The following program is your's with comments removed, shorted usage function, substituted exit
for return
and added printing offending option with just printf("%s", argv[opting - 1]);
下面的程序是您与删除评论,短路使用功能,取代
exit
的return
,并添加打印违规选项只printf("%s", argv[opting - 1]);
: :
#include <ctype.h>
#include <getopt.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int debug = 0;
void usage(void) { printf("-- insert usage here --\n"); }
void parseargs(int argc,char **argv)
{
static struct option long_options[] = {
{ "help", no_argument, 0, 'h' },
{ "version", no_argument, 0, 'v' },
{ 0, 0, 0, 0 }
};
int long_index = 0;
int opt;
int errorCount = 0;
optind = 1;
while((opt = getopt_long_only(argc, argv, ":d:hqv", long_options, &long_index)) != -1) {
switch (opt) {
case 'd':
debug = atoi(optarg);
break;
case 'h':
usage();
return;
break;
case 'q':
break;
case 'v':
break;
case ':':
fprintf(stderr, "[ERROR] Option '-%c' is missing argument.\n", optopt);
++errorCount;
break;
case '?':
if (optopt == 0) {
fprintf(stderr, "[ERROR] Unknown option '%s'.\n", argv[optind - 1]);
} else {
fprintf(stderr, "[ERROR] Error parsing option '-%c'\n", optopt);
}
++errorCount;
break;
}
}
if ( errorCount > 0 ) {
usage();
return;
}
}
int main(int argc, char **argv)
{
#define SIZE(x) (sizeof(x)/sizeof(*x))
struct {
int argc;
char **argv;
} tests[] = {
{ 2, (char*[]){ argv[0], (char[]){"-z"}, NULL, } },
{ 2, (char*[]){ argv[0], (char[]){"-d"}, NULL, } },
};
for (int i = 0; i < SIZE(tests); ++i) {
printf("\n## test tests[i].argv[1] = %s\n", tests[i].argv[1]);
parseargs(tests[i].argc, tests[i].argv);
}
return 0;
}
Outputs: 输出:
## test tests[i].argv[1] = -z
[ERROR] Unknown option '-z'.
-- insert usage here --
## test tests[i].argv[1] = -d
[ERROR] Option '-d' is missing argument.
-- insert usage here --
If optstring is set to "d:hqv"
without the leading :
, then it falls into the ?
如果optstring设置为
"d:hqv"
而没有前导:
,则它会落入?
case, ie. 案例,即。 then the program returns:
然后程序返回:
## test tests[i].argv[1] = -z
./a.out: unrecognized option '-z'
[ERROR] Unknown option '-z'.
-- insert usage here --
## test tests[i].argv[1] = -d
./a.out: option requires an argument -- 'd'
[ERROR] Error parsing option '-d'
-- insert usage here --
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.