I have a question about the error handling of getopt in C:
#include <unistd.h>
#include <getopt.h>
void showFunction()
{
printf("show function\n");
}
void printHelp()
{
printf("print help info\n");
}
#define HELP 1
#define SHOW_OPTION 2
int main(int argc, char *argv[])
{
const struct option long_opts[] = {{"help", no_argument, NULL, HELP},
{"show", no_argument ,NULL, SHOW_OPTION},
{NULL, 0, NULL, 0}};
int opt;
while((opt = getopt_long_only(argc, argv, "", long_opts, NULL)) != -1)
{
switch(opt) {
case HELP:
printHelp();
break;
case SHOW_OPTION:
showFunction();
break;
case '?':
printHelp();
break;
default:
printf("type base --help for details\n");
}
}
return 0;
}
this part will handle some error:
case '?':
printHelp();
break;
but if I type ./base -- or ./base - or ./base sdfs or ./base -- fsfs, it can not handle all those invalid input, so how to handle the input above? Can anyone help?
getopt functions support not only options (e.g. -h or --help) but also so-called non-option arguments. E.g. if you write ./base --show arg1 arg2, then --show is an option, but arg1 and arg2 are non-option arguments. Also, you can explicitly put -- in your options to show that all argument after these dashes is a non-option argument.
In your examples, options like -, sdfs, fsfs are non-option arguments. getopt_longonly (as well as other getopt functions) sets the optind extern variable to point to the first non-option element in the argv array.
Let's go through your examples:
./base --: as -- is the special option that shows that everything after it is not an option, this command line is perfectly valid and is equal to calling ./base without any options.
./base -: the variable optind will have the value 1, and argv[1] is "-". It treats single dash - as a non-option argument.
./base -- fsfs: here the -- is ignored as it shows that after that there are only non-option arguments, and fsfs is the first non-option argument so optind will be equal to 2 and argv[2] is "fsfs".
If you don't expect any non-option arguments, just check that the value of optind is equal to argc. If it is less then argc it means that you have some non-option argument:
if (optind < argc) {
/* at least one non-option argument exists */
printHelp();
}
Related
I'm writing a simple code making use of *argv[] parameter. I'd like to know whether I can use getopt() function for the following intent.
./myprogram -a PATH
./myprogram PATH
The program can either take merely PATH (e.g. /usr/tmp) or take -a option in addition to PATH. Can getopt() be used for this state? If can, how?
The program can either take merely PATH (e.g. /usr/tmp) or take option in addition to PATH. Can getopt() be used for this state? If can, how?
Certainly. I'm not sure where you even see a potential issue, unless its that you don't appreciate POSIX's and getopt()'s distinction between options and arguments. They are related, but not at all the same thing.
getopt() is fine with the case that no options are in fact specified, and it gives you access to the non-option arguments, such as PATH appears to be for you, regardless of how many options are specified. The usual usage model is to call getopt() in a loop until it returns -1 to indicate that no more options are available from the command line. At each step, the global variable optind variable provides the index of the next argv element to process, and after getopt() (first) returns -1, optind provides the index of the first non-option argument. In your case, that would be where you expect to find PATH.
int main(int argc, char *argv[]) {
const char options[] = "a";
_Bool have_a = 0;
char *the_path;
int opt;
do {
switch(opt = getopt(argc, argv, options)) {
case -1:
the_path = argv[optind];
// NOTE: the_path will now be null if no path was specified,
// and you can recognize the presence of additional,
// unexpected arguments by comparing optind to argc
break;
case 'a':
have_a = 1;
break;
case '?':
// handle invalid option ...
break;
default:
// should not happen ...
assert(0);
break;
}
} while (opt >= 0);
}
Using an optstring of "a" allows an argument of -a to act as a flag.
optind helps detect that only one additional argument is present.
The program can be executed as ./program -a path or ./program path
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
char op = ' ';//default value
int opt;
while ((opt = getopt(argc, argv, "a")) != -1)//optstring allows for -a argument
{
switch (opt)
{
case 'a':
op = 'a';//found option, set op
break;
default:
fprintf(stderr, "%s: unknown option %c\n", argv[0], optopt);
return 1;
}
}
if ( optind + 1 != argc)//one argument allowed besides optstring values
{
fprintf(stderr, "Usage: %s [-a] PATH\n", argv[0]);
return 1;
}
printf("%s %c\n", argv[optind], op);
return 0;
}
int main (int argc, char **argv) {
//Initialise variable for switch option
int option = 0;
//Set time to default to epoch
char *argtime = "1970-01-01 00:00:00";
//Use getopt to parse switches t and h (neither requires an argument, as epoch is defaulted to if non is supplied)
while ((option = getopt(argc, argv, "th")) != -1) {
//Switch statements for supplied switches
switch (option) {
//If -t
case 't':
//If arg supplied
if(optarg != NULL) {
//Set arg to given arg
argtime = optarg;
}
printf("-t is selected.\n");
break;
//If -h
case 'h':
printf("Help is selected\n");
break;
//If anything else
default:
printf("Option invalid\n");
return 1;
}
}
printf("The set time is %s\n", argtime);
return 0;
}
Here's some code I wrote to use getopt() to parse a command line switch, and argument. I want it argtime to default to "1970-01-01 00:00:00" if no argument is supplied, which it does, but when I use the -t switch and supply a different argument, argtime remains unaffected. So basically argtime = optarg; is not doing what it should, but I don't know why.
You need to tell getopt() about options that require an argument. As #xing described in comments, you do that by putting a colon in the option string, after the option letter that is affected. This allows getopt() to correctly handle grouped options and to recognize non-option arguments. Only if you do this can you expect option arguments to be communicated to you via optarg.
You claim in comments to want an optional option argument. POSIX-standard getopt() does not provide for that. In your particular case, it's not clear why you even want it, for what would it mean to specify a -t option without an argument? That the program should use a default? But that's what it will do if -t is omitted altogether.
Nevertheless, the way to approach the situation with POSIX getopt() is to provide two separate options, one that takes an argument and one that doesn't. For example, you might use option -T to signify whatever you wanted -t without an option to mean (getopt(argc, argv, "t:Th")). Alternatively, if you are willing to rely on GNU extensions then you can signify an optional option argument via a double colon (getopt(argc, argv, "t::h")), but this is less portable, and has slightly different semantics.
You might use optind after the while to see if there is another argument and if so, use that argument instead of the default.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char **argv) {
//Initialise variable for switch option
int option = 0;
//Set time to default to epoch
char *argtime = "1970-01-01 00:00:00";
//Use getopt to parse switches t and h (neither requires an argument, as epoch is defaulted to if non is supplied)
while ((option = getopt(argc, argv, "th")) != -1) {
//Switch statements for supplied switches
switch (option) {
//If -t
case 't':
//If arg supplied
printf("-t is selected.\n");
break;
//If -h
case 'h':
printf("Help is selected\n");
break;
//If anything else
default:
printf("Option invalid\n");
return 1;
}
}
if ( optind < argc) {//more arguments available
//Set arg to given arg
argtime = argv[optind];
}
printf("The set time is %s\n", argtime);
return 0;
}
I am trying to parse command line arguments in C. Currently, I am using getopt do the parse. I have something like this:
#include <unistd.h>
int main(int argc, char ** argv)
{
while((c=getopt(argc, argv, "abf:")) != -1)
{
switch(c)
{
case 'a':
break;
case 'b':
break;
case 'f':
puts(optarg);
break;
case ':':
puts("oops");
break;
case '?'
puts("wrong command");
break;
}
}
}
then need to use ./a.out -fto run the program, and -f is the command element, but looks like -f must start with a '-', if I do not want the command element starts with '-', i.e, using ./a.out f instead of ./a.out -f, how to achieve it?
if getopt does not support parsing a command line in this way, are there any other library to use in C?
The argc and argv variables give you access to what you're looking for. argc is "argument count" and argv is "argument vector" (array of strings).
getopt is a very useful and powerful tool, but if you must not start with a dash, you can just access the argument array directly:
int main( int argc, char** argv) {
if( argc != 1) { /* problem! */ }
char * argument = argv[1]; // a.out f ... argv[1] will be "f"
}
You could use (on Linux with GNU libc) for parsing program arguments:
getopt with getopt_long; you might skip some arguments using tricks around optind
argp which is quite powerful
and of course you could parse program arguments manually, since they are given thru main(int argc, char**argv) on Linux (with the guarantee that argc>0, that argv[0] is "the program name" -e.g. to find it in your $PATH when it contains no / ..., that argv[argc] is the NULL pointer, and that before that every argv[i] with i<argc and i>0 is a zero-terminated string. See execve(2) for more.
GNU coding standards: command line interfaces document quite clearly some conventions. Please, obey at least the --help and --version conventions!
You might also be concerned by customizing the shell auto-completion facilities. GNU bash has programmable completion. zsh has a sophisticated completion system.
Remember that on Posix and Linux the globbing of command words is done by the shell before starting your program. See glob(7).
The getopt library will stop parsing at the first non-option argument. For command-based programs, this will be at the command name. You can then set optind to the index to start at and run getopt again with the command-specific arguments.
For example:
// general getopts
if (optind >= argc) return 0; // error -- no command
if (strcmp(argv[optind], "command") == 0)
{
++optind; // move over the command name
// 'command'-specific getopts
if (optind >= argc) return 0; // error -- no input
}
This should allow git-like command-line parsing.
I'm making a program in C in linux environment. Now, program runs with arguments which I supply in the command line.
For example:
./programName -a 45 -b 64
I wanted to handle the case when my command line parameters are wrongly supplied. Say, only 'a' and 'b' are valid parameters and character other than that is wrong. I handled this case. But suppose if my command line parameter is like this:
./programName -a 45 -b
It gives segmentation fault(core dumped). I know why it gives because there is no arguments after b. But how can I handle this situation such that when this condition arrives, I can print an error message on screen and exit my program.
As per the main function wiki page:
The parameters argc, argument count, and argv, argument vector, respectively
So you can use your argc parameter to check whether or not you have the right number of arguments. If you don't have 4, handle it and proceed without segfault.
You can, and quite probably should, use getopt() or its GNU brethren getopt_long().
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int b = 0;
int a = 0;
int opt;
while ((opt = getopt(argc, argv, "a:b:")) != -1)
{
switch (opt)
{
case 'a':
a = atoi(optarg);
break;
case 'b':
b = atoi(optarg);
break;
default:
fprintf(stderr, "Usage: %s -a num -b num\n", argv[0]);
exit(1);
}
}
if (a == 0 || b == 0)
{
fprintf(stderr, "%s: you did not provide non-zero values for both -a and -b options\n", argv[0]);
exit(1);
}
printf("a = %d, b = %d, sum = %d\n", a, b, a + b);
return(0);
}
You can make the error detection more clever as you wish, not allowing repeats, spotting extra arguments, allowing zeros through, etc. But the key point is that getopt() will outlaw your problematic invocation.
We can't see what went wrong with your code because you didn't show it, but if you go accessing a non-existent argument (like argv[4] when you run ./programName -a 42 -b), then you get core dumps. There are those who write out option parsing code by hand; such code is more vulnerable to such problems than code using getopt() or an equivalent option parsing function.
I want to use getopt_long() to parse command-line arguments. After reading the getopt_long() man pages, I have the understanding that getopt_long() only can parse one argument after an option. Is there anyway to use getopt_long() to parse this command line like this:
./a.out -s 127.0.0.1 2012 -u stackoverflow
To give the result:
ip = 127.0.0.1
port = 2012
username = stackoverflow
Here's what I've tried:
while (1) {
int this_option_optind = optind ? optind : 1;
int option_index = 0;
static struct option long_options[] = {
{"server", required_argument, NULL, 's'},
{"user", required_argument, NULL, 'u'},
{0, 0, 0, 0},
};
c = getopt_long(argc, argv, "s:u:", long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 's':
printf("option %s", long_options[option_index].name);
if (optarg) {
printf(" with arg %s", optarg);
}
printf("\n");
case 'u':
printf("option %s", long_options[option_index].name);
if (optarg) {
printf(" with arg %s", optarg);
}
printf("\n");
case '?':
break;
default:
printf("?? getopt returned character code 0%o ??\n", c);
}
}
The first answer is: nothing stops you from using or modifying optind. If optind < argc then optind is the index of the next common-line argument in argv, and otherwise there are no more arguments. So you can use that argument in your processing loop, but it's your responsibility:
to ensure that optind is in range (< argc)
to check whether the argument at argv[optind] is another option or not (i.e. whether it starts with a -)
to increment optind so that the argument you've used doesn't get rescanned by getopt.
The second answer is: you should think three times before doing something non-standard like this. Although it might seem like a bit more typing, there are good reasons to use a more standard technique, like a -p PORT option. It's easier to document, less work to implement, and less surprising for users accustomed to standard command-line option behaviour.
Finally, you're missing a number of break statements in your example code, which is why -s will be reported as also being -u.
If you'd call your program like this:
./a.out -s "127.0.0.1 2012" -u stackoverflow
you'd be getting "127.0.0.1 2012" as value for optarg in the 's' case.