From https://github.com/karelzak/util-linux/blob/master/disk-utils/mkfs.c#L94-L113
/* Check commandline options. */
opterr = 0;
while ((more == 0)
&& ((i = getopt_long(argc, argv, "Vt:h", longopts, NULL))
!= -1))
switch (i) {
case 'V':
verbose++;
break;
case 't':
fstype = optarg;
break;
case 'h':
usage();
case VERSION_OPTION:
print_version();
default:
optind--;
more = 1;
break; /* start of specific arguments */
The docs for mkfs say that -V is the short flag for both version and verbose. I'm having trouble understanding how this is possible, and am looking for clarity.
VERSION_OPTION is defined as enum { VERSION_OPTION = CHAR_MAX + 1 }; so I'm not sure what char that is.
Look up a few lines. Before calling getopt_long:
if (argc == 2 && !strcmp(argv[1], "-V"))
print_version();
The code has a special case, not handled by the normal option processing code, for a single argument with the value -V. If there's more than one argument, it will fall through that if and process -V as the verbose flag.
One effect of this is that
mkfs -V -V
prints an error message:
mkfs: no device specified
Try 'mkfs --help' for more information.
This is also noted in (some versions of) the man page:
-V, --version
Display version information and exit. (Option -V will display version information only when it is the only parameter, otherwise it will work as --verbose.)
Related
int res = 0;
while ((res = getopt(argc, argv, "ivlcne")) != -1) {
switch (res)
{
case 'i':
pCurrentFlags->i = 1;
break;
case 'v':
pCurrentFlags->v = 1;
break;
default:
break;
}
}
Using such a code, I encounter a problem. If I write ./a.out -i filename -v in the terminal, the -v flag will not work.
UPDATE:
Commonly used practice to parse unordered flags is to parse all the arguments first and only after that handle them.
There is also a GNU implementation of the getopt() which supports that behavior as #Jonathan Leffler mentioned in the comments.
But from your copy, it is unclear what you are trying to do.
The v option in the code you provide needs an argument, but in the command line ./a.out -i filename -v it is not taking it.
If you need v as a non-parameter option, the easiest way to make it work is to change "ivlcne" with "i:v1:l:c:n:e" and with this way getopt() will no longer wait for an argument for v.
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 use getopt to get arguments from terminal, but it depends me on the order of arguemts. there's a difference between
./my -A -B -C
and
./my -B -A -C
is there any way to determine the order?
secondly, I'd like to determine count of the args (if there are some optional ones)
ps sorry for my english, i hope you understand what i mean
you have argv[] as a parameter passed to main() it contains an array of pointers to character strings. Those strings are in the exact order they were on the command line. The way to determine if some argument was listed before another.
is to step through the argv[] strings.
You record the order in which getopt() returns them. It returns them in left-to-right order, so there's no mystery. In the first invocation, -A is reported before -B and -C is reported last. In the second, -B is reported before -A and -C is still last. You can decide to do the processing when the arguments are read (so the actions occur while you're reading the options), or you can store the options in the sequence they appear and do the processing after the options are completely read. Both are feasible. If an error occurs with the first technique, though, the early processing will have been done. If that's unacceptable, process all the options first.
You can count the number of option arguments in your loop easily enough:
int optcnt = 0;
int opt;
while ((opt = getopt(argc, argv, "ABC")) != -1)
optcnt++;
You'd have a loop within braces with a switch as well as the simple increment, probably.
Note that you can have repeated options, possibly with a different option value on each time:
./my -D name1 -D name2 -A -B -D name3 -C
You can keep a record of which options and which names appear in sequence.
int optcnt = 0;
int opt;
int o_idx = 0;
int d_idx = 0;
char *d_opts[20];
char o_opts[20];
while ((opt = getopt(argc, argv, "ABCD:")) == -1)
{
optcnt++;
switch (opt)
{
case 'A':
case 'B':
case 'C':
o_opts[o_idx++] = opt;
break;
case 'D':
o_opts[o_idx++] = opt;
d_opts[d_idx++] = optarg;
break;
default:
…report error and exit…
break;
}
}
/* Parsing complete - do final checks */
/* Processing loop */
int d = 0;
int i;
for (i = 0; i < o_idx; i++)
{
switch (o_opts[i])
{
case 'A':
process_A();
break;
case 'B':
process_B();
break;
case 'C':
process_C();
break;
case 'D':
process_D(d_opts[d++]);
break;
}
}
assert(d == d_idx && i == o_idx);
Resetting getopt() back to the beginning is an undocumented art; try not to need to reparse your command line.
I'm trying to make sense of a section of skeleton code for a class. The intended usage would be:
./a.out -d -n Foo -i Bar
The skeleton code works fine, but I have never used getopt() and can't understand why it works correctly (understanding it has nothing to do with the assignment, I just want to make sense of it). How is it that it updates / exits the while loop? I don't see a pointer increment or the arguments passed to it in the loop change at all.
char *optString = "-d-n:-i:";
int opt = getopt(argc, argv, optString);
while (opt != -1) {
switch(opt) {
case 'd':
debug = 1;
break;
case 'n':
nameserver_flag = 1;
nameserver = optarg;
break;
case 'i':
hostname = optarg;
break;
case '?':
usage();
exit(1);
default:
usage();
exit(1);
}
opt = getopt(argc, argv, optString);
}
getopt uses global variables to store the argument index, the next character to parse and some other information. Each time you call getopt, the function checks these variables to know where it last was (or where you told it it was) and updates the variables for the next call.
Most importantly, optind stores the index in argv of the next element to be scanned.
Each call to getopt processes one more of the arguments in argv, returning the result in opt, etc. etc. What else is there to understand?
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.