I am creating this question because there is not much about how this optind gets assigned for each loop.
Man page says :
The variable optind is the index of the next element to be processed in argv. The system initializes this value to 1.
Below, I have a simple code I got from Head First C and in the code we subtract "optind" from "argc" and we get the number of leftover arguments, which will we use then to print leftover arguments as "Ingredients".
#include <unistd.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
char* delivery = "";
int thick = 0 ;
int count = 0;
char ch;,
for(int i = 0; i < argc;i++){
//This is , to show the whole array and their indexes.
printf("Argv[%i] = %s\n", i, argv[i]);
}
while((ch = getopt(argc, argv, "d:t")) != -1 ){
switch(ch) {
case 'd':
printf("Optind in case 'd' : %i\n",optind);
delivery = optarg;
break;
case 't':
printf("Optind in case 't' : %i\n",optind);
thick = 1;
break;
default:
fprintf(stderr,"Unknown option: '%s'\n", optarg); // optional argument.
return 1;
}
}
argc -= optind;
argv += optind;
printf("Optind : %i and Argc after the subctraction : %i\n",optind,argc);
if(thick)
puts("Thick crust");
if(delivery[0]){
printf("To be delivered %s\n", delivery);
}
puts("Ingredients:");
for(count = 0; count < argc ; count ++){
puts(argv[count]);
}
return 0;
}
So at the beginning of the code the for loop writes all the array and its indexes to see the difference.
Then I run the code with :
./pizzaCode -d now Anchovies Pineapple -t //-t is intentionally at the end
I was told that if the flag was at the end it wouldn't get in the 't' case but somehow it works on my ubuntu. That is another thing I wonder but not the main question.
So the output is as follows :
Argv[0] = ./pizzaCode
Argv[1] = -d
Argv[2] = now
Argv[3] = Anchovies
Argv[4] = Pineapple
Argv[5] = -t
Optind in case 'd' : 3
Optind in case 't' : 6
Optind : 4 and Argc after the subctraction : 2
Thick crust
To be delivered now
Ingredients:
Anchovies
Pineapple
1- Everything is fine so far, the problem is how come argv[0] and argv1 became Anchovies and Pineapple ?
2- And another question is how did optind become 3 in case 'd'? Since 'd's index is 1 and the next index is 2.
3- How did optind become 4 after the loop ? It was 6 in the case 't'.
I hope my question is clear for you all, I am just trying to understand the logic instead of having to memorize it.
Thank you in advance!
The manpage for Gnu getopt documents this non-standard implementation:
By default, getopt() permutes the contents of argv as it scans, so that eventually all the nonoptions are at the end.
This is actually not quite true; the permutation occurs after the last option is scanned, as you have seen in your example. But the effect is the same; argv is permuted so that nonoptions are at the end, and optind is modified to index the first nonoption.
If you want to avoid the permutation, so that getopt behaves as per Posix:
If the first character of optstring is '+' or the environment variable POSIXLY_CORRECT is set, then option processing stops as soon as a nonoption argument is encountered.
In this case, no permuting is done and optind's value is preserved.
Setting POSIXLY_CORRECT has other consequences, documented here and there in the manpages for various Gnu utilities. My habit is to use + as the first character of the option string (unless I actually want non-Posix behaviour), but arguably setting the environment variable is more portable.
For your specific questions:
Why are the non-option arguments at argv[0] and argv[1]?
Because you modified argv: argv += optind;
Why is optind 3 in the loop processing option -d?
Because that option takes an argument. So the next argument is the one following the now argument, which has already been processed (by placing a pointer to it into optarg).
How did optind become 4?
As above, it was modified after the argv vector was permuted, in order for optind to be the index of the first "unprocessed" non-option argument.
Related
I just want to test global variable optind and do the following test.How to judge the value of optind?
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
char optStr[] = "ab";
int c;
while ((c = getopt(argc, argv, optStr)) != -1) {
printf("optind: %d\n", optind);
switch (c) {
case 'a':
printf("-a\n");
break;
case 'b':
printf("-b\n");
break;
case '?':
printf("error\n");
break;
}
}
return 0;
}
./a.out -ab
optind: 1
-a
optind: 2
-b
next:
./a.out -a
optind: 2
-a
The getopt man page says:
The variable optind is the index of the next element to be processed in argv.
And below is how it results in the output you see.
1) ./a.out -ab will result in argv values as such:
argv[0]="./a.out"
argv[1]="-ab"
argv[2]=0
Start: optind=1
First getopt call: Option a is processed and optind is not changed. It stays at 1 because the a option has been processed but there is still a b option remaining.
Second getopt call: Option b is processed and now optind is set to 2 as there are no more options left in argv[1].
Now for your second example.
2) ./a.out -a will result in argv values as such:
argv[0]="./a.out"
argv[1]="-a"
argv[2]=0
Start: optind=1
First getopt call: Option a is processed and optind is set to 2 as there are no more options left in argv[1].
optind shows the index of next element to be processed in argv array.
So, when you do ./a.out -ab:
When a is evaluated, the next element to be processed in the argv is still 1, because b is not evaluated yet from argv[1].
When b is evaluated, the next element to be processed in the argv is
2, because argv[1] is completely processed.
When you do ./a.out -a:
When a is evaluated, the next element to evaluate is 2 because argv[1] is completely processed.
This is more clearly seen if you increase your options to say. optstr[] = "abcdef;". And call your program as ./a.out -abcdef. The optnid will be 2 only when the last option - i.e. f - is evaluated. Also, call ./a.out -a -b -c to see how optnid shows the next element to be processed in argv array.
I am trying to understand how to use command line options with a command line c tool and I came accross this example.Can some one explain how the code flow works,I am not able to understand it,also I understand that it uses a getopt() function which is inbuilt.
The exe called is rocket_to and it has two command line options, e and a. e option takes 4 as an argument and a option takes Brasalia,Tokyo,London as argument.
Can some one explain how the code works?
This is the actual code:
command line:
rocket_to -e 4 -a Brasalia Tokyo London
code:
#include<unistd.h>
..
while((ch=getopt(argc,argv,"ae:"))!=EOF)
switch(ch){
..
case 'e':
engine_count=optarg;
..
}
argc -=optind;
argv +=optind;
There are many manual pages for getopt() including the POSIX specification. They describe what the getopt() function does. You can also read the POSIX Utility Conventions which describes how arguments are handled by most programs (but there are plenty of exceptions to the rules, usually because of historical, pre-POSIX precedent).
In the example outline code, the -e option takes an argument, and that is the 4 in the example command line. You can tell because of the e: in the third argument to getopt() (the colon following the letter indicates that the option takes an argument). The -a option takes no argument; you can tell because it is not followed by a colon in the third argument to getopt(). The names Brasilia, Tokyo, London are non-option arguments after the option processing is complete. They're the values in argv[0] .. argv[argc-1] after the two compound assignments outside the loop.
The use of EOF is incorrect; getopt() returns -1 when there are no more options for it to process. You don't have to include <stdio.h> to be able to use getopt().
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int ch;
int aflag = 0;
char *engine_count = "0";
while ((ch = getopt(argc, argv, "ae:")) != -1)
{
switch (ch)
{
case 'a':
aflag = 1;
break;
case 'e':
engine_count = optarg;
break;
default:
fprintf(stderr, "Usage: %s [-a][-e engine] [name ...]\n", argv[0]);
exit(EXIT_FAILURE);
}
}
argc -= optind;
argv += optind;
printf("A flag = %d\n", aflag);
printf("Engine = %s\n", engine_count);
for (int i = 0; i < argc; i++)
printf("argv[%d] = %s\n", i, argv[i]);
return 0;
}
That is working code which, if compiled to create a program rocket_to, produces:
$ ./rocket_to -e 4 -a Brasilia Tokyo London
A flag = 1
Engine = 4
argv[0] = Brasilia
argv[1] = Tokyo
argv[2] = London
$ ./rocket_to -a -e 4 Brasilia Tokyo London
A flag = 1
Engine = 4
argv[0] = Brasilia
argv[1] = Tokyo
argv[2] = London
$ ./rocket_to -e -a 4 Brasilia Tokyo London
A flag = 0
Engine = -a
argv[0] = 4
argv[1] = Brasilia
argv[2] = Tokyo
argv[3] = London
$
From the getopt man page:
The getopt() function parses the command-line arguments. Its arguments argc and argv are the argument count and array as passed to
the main() function on program invocation. An element of argv that starts with '-' (and is not exactly "-" or "--") is an option element. The characters of this element (aside from the initial '-') are option characters. If getopt() is called repeatedly, it
returns successively each of the option characters from each of the option elements.
The 3rd argument to getopt() are the valid options. If the option is followed by a colon it requires an argument. The argument can be accessed through the optarg variable. So in your example you have two options: 'a' which takes no argument and 'e' which takes an argument.
If getopt() finds an options it returns the character. If all options are parsed it returns -1 and if an unknown option is found it returns -1.
So your code loops through all options and processes them in a switch statement.
Next time when you have trouble understanding something like this try to run man <unknown function> first.
I have a program that takes in optional arguments. The necessary arguments are a file and integers (1 or more). The optional arguments are a mix of strings and integers.
So a correct input on the command line could be:
./main trace_file 8 12 # (only necessary arguments)
./main –n 3000000 –p page.txt trace_file 8 7 4 # (with optional arguments)
I need to get the integers after trace_file into an array. I'm having trouble figuring out how to do this when the optional arguments are enabled, because another integer is on the command line. A push in the right direction would be greatly appreciated, because I cannot figure out how to do this.
EDIT:
so far, all I have for parsing the arguments is this:
for(j=2, k=0; j<argc; j++, k++) {
shift += atoi(argv[j]);
shiftArr[k] = 32 - shift;
bitMaskArr[k] = (int)(pow(2, atoi(argv[j])) - 1) << (shiftArr[k]);
entryCnt[k] = (int)pow(2, atoi(argv[j]));
}
But this will only work when no optional arguments are entered.
I don't see any major problems if you use a reasonably POSIX-compliant version of getopt().
Source code (goo.c)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
/*
./main trace_file 8 12 # (only necessary arguments)
./main –n 3000000 –p page.txt trace_file 8 7 4 # (with optional arguments)
*/
static void usage(const char *argv0)
{
fprintf(stderr, "Usage: %s [-n number][-p pagefile] trace n1 n2 ...\n", argv0);
exit(EXIT_FAILURE);
}
int main(int argc, char **argv)
{
int number = 0;
char *pagefile = "default.txt";
char *tracefile;
int opt;
while ((opt = getopt(argc, argv, "n:p:")) != -1)
{
switch (opt)
{
case 'p':
pagefile = optarg;
break;
case 'n':
number = atoi(optarg);
break;
default:
usage(argv[0]);
}
}
if (argc - optind < 3)
{
fprintf(stderr, "%s: too few arguments\n", argv[0]);
usage(argv[0]);
}
tracefile = argv[optind++];
printf("Trace file: %s\n", tracefile);
printf("Page file: %s\n", pagefile);
printf("Multiplier: %d\n", number);
for (int i = optind; i < argc; i++)
printf("Processing number: %d (%s)\n", atoi(argv[i]), argv[i]);
return 0;
}
Compilation
$ gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
> -Wold-style-definition -Werror goo.c -o goo
Example runs
$ ./goo trace_file 8 12
Trace file: trace_file
Page file: default.txt
Multiplier: 0
Processing number: 8 (8)
Processing number: 12 (12)
$ ./goo -n 3000000 -p page.txt trace_file 8 7 4
Trace file: trace_file
Page file: page.txt
Multiplier: 3000000
Processing number: 8 (8)
Processing number: 7 (7)
Processing number: 4 (4)
$
I've given this a bit of thought and there is no simple way for getopt to return multiple arguments. I did wonder about defining "y::",but have dismissed this. Options are
1) have 2 options y and Y use one for each int and a boolean flag to catch the exception where y is defined, but Y isn't.
2) leave complex options like y out of the getopt cycle. Once getopt has processed options and shuffled arguments. Pre process these arguments to capture the -y operands and code in the process arguments to skip -y and it's operands
3) it's more usual across *nix commands to provide multiple values as a single argument which itself is a comma separated list of values. You achieve this by adding "y:" to your getopt processing. The string it points too needs to be parsed into the 2 tokens A and B from string A,B
If you can't use getopt() or some other function that does the hard work for you, then a possible strategy would be:
Create new variables myargc, myargv, and copy argc and argv into them.
Write functions that deal with paired arguments (like "-n 300000" or "-p page.txt". Pass myargc and myargv into these functions by reference. Each such function returns the value associated with the argument (e.g., 300000 or page.txt) if it was found, or an invalid value (e.g., -1 or "") if it wasn't. And in either case, the function removes both items from myargv and decrements myargc by 2.
If you also have arguments that are just individual flags, write functions for those that return boolean indicating whether the flag was found, remove the flag from myargv, and decrements myargc by 1. (You could deal with trace_file this way, even if it's not optional. I'm assuming that trace_file is just a flag, and not related to the 8 that follows it.)
Call the functions for each of your optional arguments first. The stuff that's left in myargc and myargv after you've called them all should just be your required arguments, and you can process them as you normally would.
If you can't use getopt() or some other function that does the hard work for you, then a possible strategy would be:
Create new variables myargc, myargv, and copy argc and argv into them.
Write functions that deal with paired arguments (like "-n 300000" or "-p page.txt". Pass myargc and myargv into these functions by reference. Each such function returns the value associated with the argument (e.g., 300000 or page.txt) if it was found, or an invalid value (e.g., -1 or "") if it wasn't. And in either case, the function removes both items from myargv and decrements myargc by 2.
If you also have arguments that are just individual flags, write functions for those that return boolean indicating whether the flag was found, remove the flag from myargv, and decrements myargc by 1. (You could deal with trace_file this way, even if it's not optional. I'm assuming that trace_file is just a flag, and not related to the 8 that follows it.)
Call the functions for each of your optional arguments first.
I enter the following command line:
./file -a 1 -b2 -a5 -b 55 -b4
The output I get is:
a: 1
argv[1]: -a
b: 2
argv[2]: 1
a: 5
argv[3]: -b2
b: 55
argv[4]: -a5
b: 4
argv[5]: -b
Counter: 5
The output I wish to get should be:
a: 1
argv[1]: -a 1
b: 2
argv[2]: -b2
a: 5
argv[3]: -a5
b: 55
argv[4]: -b 55
b: 4
argv[5]: -b4
Counter: 5
Arguments with a space are currently counted as 2 arguments. I would like my program to count it as 1 argument only (I want it sees "-a 1". Not "-a" and "1" separately).
This is the source code I use I get the outputs:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
int main(int argc, char *argv[])
{
int opt = 0;
int quantum1 = 0, quantum2 = 0;
int counter = 0;
while ((opt = getopt(argc, argv,"a:b:")) != -1)
{
switch (opt)
{
case 'a' :
quantum1 = atoi(optarg);
printf("a: %d\n", quantum1);
break;
case 'b' :
quantum2 = atoi(optarg);
printf("b: %d\n", quantum2);
break;
default:
printf("Error\n");
return 1;
break;
}
counter++;
printf("argv[%d]: %s\n", counter, argv[counter]);
}
printf("Counter: %d\n", counter);
return 0;
}
Note: The quotation marks suggested work, but I'm not allowed to use quotation marks or any other symbols.
Use
./file "-a 1" -b2 -a5 "-b 55" -b4
to make "-a 1" as the first argument and "-b 55" as the fourth argument.
If you are now allowed to use quotation marks, you can escape the space in linux using:
./file -a\ 1 -b2 -a5 -b\ 55 -b4
The problem is not in the way the arguments are being parsed. The problem is the way the code displays the arguments. Specifically, when a space exists between the option and the argument, the optind will advance by two. optind is an external variable that getopt uses to keep track of the next index into the argv array.
So if you simply eliminate the counter and the lines shown below, you'll find that your code is already working correctly.
counter++;
printf("argv[%d]: %s\n", counter, argv[counter]);
printf("Counter: %d\n", counter);
If you absolutely must have a count of the number of arguments found, then simply update the counter in each case statement, e.g.
case 'a' :
counter++;
...
case 'b' :
counter++;
...
and then print the counter at the end. It's the printf("argv[%d]: %s\n", counter, argv[counter]); line that's getting you confused. That line serves no useful purpose and should be removed.
For a command line of ./test -a1 -b 2, the strings in argv will be
argv[0]: "./test"
argv[1]: "-a1"
argv[2]: "-b"
argv[3]: "2"
When you call getopt the first time, it will read argv[1] and will recognize the -a as one of the options that you've specified, so it splits the string into two parts and returns the 'a' while setting the optarg to "1".
When you call getopt the second time, it will read argv[2] and will recognize the -b option. Since you've specified that -b takes an argument, but argv[2] doesn't contain the argument, getopt will take argv[3] as the argument. Hence it returns the 'b' while setting the optarg to "2".
The bottom line is that getopt is designed to ignore the white space between the option and its argument. It does that by processing two of the argv strings if the user put white space between the option and its argument.
My argument is like this
./a.out -i file1 file2 file3
How can I utilize getopt() to get 3 (or more) input files?
I'm doing something like this:
while ((opt = getopt(argc, argv, "i:xyz.."))!= -1){
case 'i':
input = optarg;
break;
...
}
I get just the file1; how to get file2, file3?
I know this is quite old but I came across this in my search for a solution.
while((command = getopt(argc, argv, "a:")) != -1){
switch(command){
case 'a':
(...)
optind--;
for( ;optind < argc && *argv[optind] != '-'; optind++){
DoSomething( argv[optind] );
}
break;
}
I found that int optind (extern used by getopt() ) points to next position after the 'current argv' selected by getopt();
That's why I decrease it at the beginning.
First of all for loop checks if the value of current argument is within boundaries of argv (argc is the length of array so last position in array argv is argc-1).
Second part of && compares if the next argument's first char is '-'. If the first char is '-' then we run out of next values for current argument else argv[optind] is our next value. And so on until the argv is over or argument runs out of values.
At the end increment optind to check for the next argv.
Note that because we are checking 'optind < argc' first second part of condition will not be executed unless first part is true so no worries of reading outside of array boundaries.
PS I am a quite new C programmer if someone has an improvements or critique please share it.
If you must, you could start at argv[optind] and increment optind yourself. However, I would recommend against this since I consider that syntax to be poor form. (How would you know when you've reached the end of the list? What if someone has a file named with a - as the first character?)
I think that it would be better yet to change your syntax to either:
/a.out -i file1 -i file2 -i file3
Or to treat the list of files as positional parameters:
/a.out file1 file2 file3
Note that glibc's nonconformant argument permutation extension will break any attempt to use multiple arguments to -i in this manner. And on non-GNU systems, the "second argument to -i" will be interpreted as the first non-option argument, halting any further option parsing. With these issues in mind, I would drop getopt and write your own command line parser if you want to use this syntax, since it's not a syntax supported by getopt.
I looked and tried the code above, but I found my solution a little easier and worked better for me:
The handling I wanted was:
-m mux_i2c_group mux_i2c_out
(2 arguments required).
Here's how it panned out for me:
case 'm':
mux_i2c_group = strtol(optarg, &ch_p, 0);
if (optind < argc && *argv[optind] != '-'){
mux_i2c_out = strtol(argv[optind], NULL, 0);
optind++;
} else {
fprintf(stderr, "\n-m option require TWO arguments <mux_group> "
"<mux_out>\n\n");
usage();
}
use_mux_flag = 1;
break;
This grabbed the first value form me as normal and then just looked for the second, REQUIRED value.
The solution by GoTTimw has proven very useful to me. However, I would like to mention one more idea, that has not been suggested here yet.
Pass arguments as one string in this way.
./a.out -i "file1 file2 file3"
Then you get one string as a single argument and you only need to split it by space.