Combine options with arguments getopt - c

hello i am writing a program that takes in multiple options from the command line in c. these are some of the options:
-l counts number of lines in file
-w counts words in file
-c counts characters in file
so to make them work, you could do:
./program -l file.txt
./program -w file.txt
./program -c file.txt
however i want to combine them so that multiple options still do as they should. e.g.:
./program -lwc file.txt
will count lines, words, AND files,
./program -lw file.txt
will count lines AND words,
./program -cl file.txt
will count characters AND lines, etc. i just cant figure out how to do this. this is my code for the arguments:
while ( ( c = getopt( argc, argv, "c:w:l:" ) ) != -1 ) { /* get options */
switch ( c ) {
case 'c':
cflag = 1;
break;
case 'w':
wflag = 2;
break;
case 'l':
lflag = 3;
break;
}
}
is it simply changing options around but it still doesnt work. with the code i have now, combining two options results in a segment fault. any help is appreciated

As Jens Gustedt said: remove the colons from the options to make them not take an argument. Then, when option-processing is done (i.e. getopt returned -1), check if optind is less than argc meaning there was an extra argument (your file-name). Like this:
/* after the while-loop */
if(optind < argc){
fileName = argv[optind];
optind++;
}else{
perror("No file specified");
return -1;
}
When getopt is done optind points to the first entry in argv that wasn't processed by getopt.

The : in the getopt format are for options with arguments. Delete them.

Related

Getopt - adding case for both missing option and missing non-option argument

I'm making a part of a bigger program, this part takes in information from a text file or a feed of standard input, the nature of this information depends on what option is added to the program. With no text file added as a non-option argument, the program should read from stdin.
For example:
fizzle.exe -a textfile.txt reads textfile.exe in the way prescriped by the option -a
fizzle.exe -a reads stdin in the way prescribed by the option a
What I havent gotten to work yet is:
fizzle.exe textfile.txt reading textfile.txt(or stdin if no proper text file) in the way prescribed by giving no option
This is my code:
while ((option = getopt(argc,argv, ":a:b:")) != -1 ) {
FILE *pointerFile = filereader(optarg);
switch(option) {
case 'a' :
optionchosen = 0;
counter(optionchosen,pointerFile,argv);
break;
case 'b' :
optionchosen = 1;
counter(optionchosen,pointerFile,argv);
break;
case ':' :
counter(optionchosen,pointerFile,argv);
break;
}
}
I cant figure out how to add a case to this switch that is activated by giving no option, but still works with or without the non-option argument(filename).
Well, you can do exactly what you want to do with getopt, and more, you just need to adjust your logic slightly. Specifically, you can setup getopt so that:
fizzle.exe /* reads from stdin */
fizzle.exe -a /* reads from stdin */
fizzle.exe -a textfile.txt /* reads textfile.txt */
fizzle.exe textfile.txt /* reads textfile.txt */
Here is a short example of implementing the above logic around the -a option. The last case also specifies that, if give, the first unhandled option after all options are given will also be taken as the filename to read.
#include <stdio.h>
#include <unistd.h> /* for getopt */
int main (int argc, char **argv) {
int opt;
char *fn = NULL;
FILE *pointerFile = stdin; /* set 'stdin' by default */
while ((opt = getopt (argc, argv, "a::b:")) != -1) {
switch (opt) {
case 'a' : /* open file if given following -a on command line */
if (!optarg) break; /* if nothing after -a, keep stdin */
fn = argv[optind - 1];
pointerFile = fopen (fn, "r");
break;
case 'b' :; /* do whatever */
break;
default : /* ? */
fprintf (stderr, "\nerror: invalid or missing option.\n");
}
}
/* handle any arguments that remain from optind -> argc
* for example if 'fizzle.exe textfile.txt' given, read from
* textfile.txt instead of stdin.
*/
if (pointerFile == stdin && argc > optind) {
fn = argv[optind++];
pointerFile = fopen (fn, "r");
}
printf ("\n fizzle.txt reads : %s\n\n", pointerFile == stdin ? "stdin" : fn);
return 0;
}
Example Use/output
$ ./bin/optfizzle
fizzle.exe reads : stdin
$ ./bin/optfizzle -a
fizzle.exe reads : stdin
$ ./bin/optfizzle -a somefile.txt
fizzle.exe reads : somefile.txt
$ ./bin/optfizzle someotherfile.txt
fizzle.exe reads : someotherfile.txt
Look it over and let me know if you have any questions.
Forget the leading : in your getopt string; just use getopt for options. After you're done with them, the remaining elements on the command line, if any, are found between argv[optind] and argv[argc].
If the caller provides no filename, use /dev/stdin as the default, or stdin from stdlib.h. No need to require the user to provide a - to stand in for standard input.
Programs using getopt should, by convention, read from stdin if you give it a - as an input parameter.
As a side note, you should always have a case for ? (or a default case).
Here is an example of reading the remaining arguments, place it below the big while loop where you process all getopt flags:
int i;
for (i = optind; i < argc; ++i)
printf("Non-option argument %s\n", argv[i]);

How to use command line options in a c command line tool?

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.

how to determine order and count of arguments in getopt

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.

how to parse command line arguments in C?

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.

C getopt multiple value

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.

Resources