Я пытаюсь использовать getopt_long_only()
с пользовательскими сообщениями об ошибках.Код показан ниже.Я попытался установить opterr = 0 и использовать двоеточие в начале строки optstring, чтобы отключить встроенные сообщения об ошибках.Я добавил блок кода, контролируемый с помощью логического optoptWorks = true
, чтобы попытаться настроить сообщения об ошибках, например, чтобы напечатать сообщение, когда используется неверный параметр, такой как -z
.Однако optopt
всегда имеет значение 0, и сообщение об ошибке ?
, которое я использую, не имеет смысла.Ошибки ':' (в двоеточии) (отсутствующий аргумент, такой как -d
) работает нормально для пользовательских сообщений.Отключение встроенных сообщений об ошибках и их обработка в ?
, по-видимому, приводит к тому, что optopt
всегда устанавливается в 0, поэтому я не могу напечатать параметр, вызывающий оскорбления (-z is not recognized
).Я скомпилировал на Debian Linux gcc 4.9.4, а также Cygwin gcc 7.3.0, и оба дали тот же результат.Похоже, getopt_long_only()
может не установить optopt
правильно или я что-то упустил?Многие примеры в Интернете позволяют обойти это, используя либо встроенные сообщения об ошибках, либо просто используя печать, не сообщая пользователю, какая опция не распознана.
Здесь выводится 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.
и здесь выводится с 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.
Код следующий:
/*
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);
}