Let's say I made a C program that is called like this:
./something -d dopt filename
So -d is a command, dopt is an optional argument to -d and filename is an argument to ./something, because I can also call ./something filename.
What is the getopt form to represent get the filename?
Use optstring "d:"
Capture -d dopt with optarg in the usual way. Then look at optind (compare it with argc), which tells you whether there are any non-option arguments left. If so, your filename is the first of these.
getopt doesn't specifically tell you what the non-option arguments are or check the number. It just tells you where they start (having first moved them to the end of the argument array, if you're in GNU's non-strict-POSIX mode)
Check-out how grep does it. At the end of main() you'll find:
if (optind < argc)
{
do
{
char *file = argv[optind];
// do something with file
}
while ( ++optind < argc);
}
The optind is the number of command-line options found by getopt. So this conditional/loop construct can handle all of the files listed by the user.
Related
I am trying to write a limited version of ls w/ some options.
However, I am stuck on the problem of parsing out my options from my arguments in a clean manner.
For example:
$ ls -l -t somefile anotherFile
$ ls somefile -lt anotherFile
have the same behavior.
This poses two problems for me:
It makes using argc a bit more difficult. For example I would consider the arguments ls -lt and ls to both have 0 arguments (other than the name of the command) however argc counts -l as an argument.
Therefore the naive implementation of :
if( argc == 1) {list all the contents of cwd}
does not work.
Is there a built-in way to get the options as well as the option count, or do I have to roll my own function?
I have to consider all the different ways options can be arranged and be careful not to get an option mixed up as a file name or directory name. It seems like the cleanest solution is to separate the options from the file arguments from the start. Is there an idiomatic way to do this / is there standard library calls that do this?
There is no built-in argument parsing help, but getopt is the "standard" method for argument parsing.
For simple apps, I sometimes roll my own with something like:
int pos=0;
argc--;argv++;
while (argc > 0) {
if (*argv[0]=='-') {
switch ((*argv)[1]) {
case 'l': //-l argument
save_option_l(++argv);
argc--; //we consumed one name
break;
//... other -options here ...
default:
usage("unrecognized option %s", *argv);
}
}
else {
save_positional_argument(argv,pos++);
}
argv++;
argc--;
}
In this case, I require the modifiers to directly follow the flags. Don't support variable usage like your first example, unless there are very strong reasons to do so.
If you have Gnu's implementation of getopt, it will do all that for you.
Posix standard getopt terminates option processing when it hits the first non-option argument. That conforms to Posix guidelines for utility argument parsing, and many of us prefer this behaviour. But others like the ability to intermingle options and non-options, and that's the norm for Gnu utilities unless you set an environment variable with the ungainly name POSIXLY_CORRECT.
Consistent with that preference, Gnu getopt parses arguments:
The default is to permute the contents of argv while scanning it so that eventually all the non-options are at the end. This allows options to be given in any order, even with programs that were not written to expect this.
Note the wording about permuting arguments. This means that if you start with
ls somefile -lt anotherFile
Gnu getopt will:
Report a l
Report a t
Report end of options (-1), leaving optind with the value 2 and argv now looking like:
ls -lt somefile anotherFile
So now you can process your non-option arguments with:
for (int argno = optind; argno < argc; ++argno) {
/* Do something with argv[argno] */
}
Also, you can tell how many non-option arguments you received with argc-optind, and if argc == optind, you know there weren't any.
Unbundling -lt into two options is standard Posix getopt behaviour. You can combine options lime that as long as the first one doesn't take an argument.
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 understand how to use getopt to accept command-line arguments like
./program -a yes -b no
What I am currently trying to do is accept command-line arguments where some are optional and some are not.
For example:
./program argv[1] argv[2] -a yes -b no
Options after multiple optional arguments is probably bad idea; don't design the command line syntax that way, if you can help it.
That said, you can parse the arguments yourself outside of getopt until you see something which looks like an option (while incrementing argv and decrementing argc). Then use getopt for the remainder of the command line from that point on.
Pseudo-code:
for (; *argv; argc--, argv++) {
if (argv looks like an option)
break;
process *argv somehow
}
now process with getopt(argc, argv, ...)
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.
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.