What is the best way to handle multiple integer arguments while using getopt? I have to be able to enter something like this on the command line: calc -a 100 50 and it will add up 100 and 50.
while ((opt = getopt(argc, argv, "adms")) != -1)
{
value = atoi(argv[optind + 1]);
printf("value : %d\n", value);
switch (opt)
{
case 'a': aFlag = 1; num1 = atoi(argv[optind]);
sum = num1 + value;
printf("%d + %d = %d\n" , num1, value, sum);
break;
case 'd': dFlag = 1;
break;
case 'm': break;
case 's': break;
default:
fprintf(stderr, "%s: unknown option %c\n", argv[0], optopt);
return 1;
}
}
if (aFlag == 1)
{
num1 = atoi(argv[optind]);
sum = num1 + value;
printf("%d + %d = %d\n" , num1, value, sum);
}
if (dFlag == 1)
{
num2 = atoi(argv[optind]);
sub = num2 / value;
printf("%d / %d = %d\n" , num2, value, sum);
}
I think that the interface you're after is probably:
./program -a 100 50
which accepts a single option -a (alternatives -m, -s and -d), and then processes the other arguments as a list of numbers, rather than as values attached to the option. This works fine unless you planned to be able to use:
./program -a 100 50 -m 20 30
to do addition and multiplication.
What do you want to do if someone can't make their mind up and tries:
./program -am 100 50
Should it be an error, or should -a or -m win? You can argue for any of those with some degree of coherency. I'd go with an error, though.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static int op_add(int a, int b) { return a + b; }
static int op_sub(int a, int b) { return a - b; }
static int op_mul(int a, int b) { return a * b; }
static int op_div(int a, int b) { return a / b; }
int main(int argc, char **argv)
{
int (*fun)(int, int) = 0;
char op;
int opt;
while ((opt = getopt(argc, argv, "adms")) != -1)
{
if (fun != 0)
{
fprintf(stderr, "%s: you can't use two operations\n", argv[0]);
return 1;
}
switch (opt)
{
case 'a': fun = op_add; op = '+'; break;
case 'd': fun = op_div; op = '/'; break;
case 'm': fun = op_mul; op = '*'; break;
case 's': fun = op_sub; op = '-'; break;
default:
fprintf(stderr, "%s: unknown option %c\n", argv[0], optopt);
return 1;
}
}
if (fun == 0 || optind + 2 != argc)
{
fprintf(stderr, "Usage: %s [-adms] num1 num2\n", argv[0]);
return 1;
}
int res = fun(atoi(argv[optind]), atoi(argv[optind+1]));
printf("%s %c %s = %d\n", argv[optind], op, argv[optind+1], res);
return 0;
}
The error checking on the values in the 'numeric' arguments is reprehensibly sloppy (meaning, non-existent). There's no division by zero check, either. But that at least produces plausible answers. It would probably be better to have an array of structures saving the function pointer and the operation character, but that sort of refinement is for you to deal with.
If you need a negative number, use -- to mark the end of the options:
$ ./program -m -- -39 -75
-39 * -75 = 2925
$
Just in case you're not sure about it, int (*fun)(int, int) is a pointer to a function that takes two int arguments and returns an int result. It allows the code to select which function to execute at runtime.
With getopt(), an option can take at most one argument. If you want your program to handle its command line with getopt(), and it must accept arguments such as you describe
calc -a 100 50
, and you must not repeat options, then the only viable solution is to make one or both of the integers non-option arguments instead of arguments to the -a option. I'd suggest making both (or all) non-option arguments. Thus, you might have something like this:
int result = getopt(argc, argv, "asmd");
switch (result) {
case 'a':
// careful: getopt() will be confused by negative numbers
if (getopt(argc, argv, "asmd") != -1) {
fprintf(stderr, "usage: %s -a|-s|-m|-d <operands>\n", argv[0]);
exit(1);
}
do_add(argv + optind);
break;
case 's':
// ...
}
After getopt() returns -1, optind is the index in argv one past that of the last option (+/- option argument) processed.
This excerpt from the man page for getopt() clearly shows that each of the answers (and the original question) are not showing an understanding of getopt:
optstring is a string containing the legitimate option characters.
If such a character is followed by a colon,
[then] the option requires an argument,
so getopt() places a pointer
to the following text in the same argv-element,
or the text of the following argv-element,
in optarg.
your code should also allow for the following:
If getopt() does not recognize an option character,
it prints an error message to stderr,
stores the character in optopt,
and returns '?'.
and finally this (which will allow for the - to be in any position on the command line:
By default, getopt() permutes the contents of argv as it scans, so that
eventually all the nonoptions are at the end.
Related
This is my first post on this site so please forgive any formatting errors.
I am coding a small program to get simple information from a file like amount of words, sorting it etc. However I started simply by using getopt since I want to use the command line to parse my commands. This is what I have so far.
#include <stdio.h>
#include <stdlib.h>
int value = 0;
int main(int argc, char **argv) {
extern char *optarg;
extern int optind;
int c, err = 0;
int cflag = 0, sflag = 0, fflag = 0;
char *substring;
// usage from instructions in case of error
static char usage[] = "usage: mywords [-cs] [-f substring] filename";
// only f flag requires additional input
while ((c = getopt(argc, argv, "csf:")) != -1)
switch (c) {
case 'c':
cflag = 1;
break;
case 's':
sflag = 1;
break;
case 'f':
fflag = 1;
printf("Before assigning to substring\n");
substring = optarg;
printf("After\n");
break;
case '?':
err = 1;
break;
}
if (fflag = 1) {
printf("%c ", &substring);
}
}
I will then input something like this to the command line after I run my makefile (which runs successfully with no errors):
mywords -f test
and ideally the output would be
test
The printf statements I have in the fflag are simple test prints to see if I was even reaching there. Thank you for your help in advance.
EDIT
It was fixed. David Collins had the right answer. Thank you so much everyone
I have noticed three issues in you code. (There may be other small issues but addressing the following should get you up and running I think.)
Testing for equality
You should use if (fflag == 1) instead of if (fflag = 1).
Pointer de-referencing
If you want to retrieve the value of the character pointed to by substring, you would use *substring instead of &substring.
But you probably don't want to do this. See below.
printf() format specifiers
When printing a string / character array, use the %s specifier instead of %c. (%c is for single characters). So you should have
if (fflag == 1) {
printf("%s ", substring);
}
Or - more simply - just
if (fflag) {
puts(substring)
}
since any non-zero value evaluates to true in C (and you initialize fflag to zero at the start of your program).
Consider a file ArgumentFile.txt
int a=100;
int b[3] = { 5, 2, 5 };
double c = 0.0014;
And the main code code.c
int main(int argc, char *argv[])
{
if (argc > 1) FILE *f = fopen(argv[1], "r");
ParseFile(f); // Set the parameters based on file
DoStuff(a,b,c); // Run the process based on the parsed arguments
}
A user could then pass arguments by doing
./CodeExecutable ArgumentFile.txt
Is there a standard solution to parse arguments from file? It would be an equivalent of getopt which parse arguments from command line?
You do not need an equivalent to getopt() you can use exactly getopt(). The getopt() function does not specifically process command line arguments; it will process any array of pointers to strings in the style of command line arguments.
#define MAX_ARGS 256
#define MAX_FILE_LEN 4096
int main(int argc, char *argv[])
{
if( argc > 1 )
{
FILE *f = fopen(argv[1], "r");
if( f != 0 )
{
char fargs[MAX_FILE_LEN] = "" ;
fread( fargs, 1, MAX_FILE_LEN, f ) ;
// Build fargv from file content
char* fargv[MAX_ARGS] ;
int fargc = 0 ;
fargv[fargc] = strtok( fargs, " \n\r" ) ;
while( fargc < MAX_ARGS && fargv[fargc] != 0 )
{
fargc++ ;
fargv[fargc] = strtok( 0, "\n\r" ) ;
}
// Process fargv using getopt()
while( (char c = getopt( fargc, fargv, "a:b:c:")) != -1 )
{
switch( c )
{
...
}
}
}
}
...
return 0 ;
}
It is probably better to dynamically allocate fargs using teh actual file length, but the above is illustrative only.
Your input file might then look like:
-a 100
-b 5,2,5
-c 0.0014
The getopt() loop will then have to process the arguments as necessary - using sscanf() for example.
switch( c )
{
case 'a' : sscanf( optarg, "%i", a ) ; break ;
case 'b' : sscanf( optarg, "%i,%i,%i", b[0], b[1], b[2] ) ; break ;
case 'c' : sscanf( optarg, "%f", c ) ; break ;
}
DoStuff( a, b, c ) ;
I use getopt(). Here is an example that allows for some more flexibility. This example demonstrates how th handle for optional optarg and multiple optargs.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
void usage(void)
{
printf("usage: \n"
"This example demonstrates how to add flexibility to the traditional linux getopt()\n"
"This help text is printed if the program is executed without arguments\n"
"or with an invalid argument configuration.\n"
"to view the help file run without arguments or with -h\n"
"Oterwise the program accepts two options: -d, -u\n"
"-d: can come with 0 or one option argument\n"
"-u: can come with one or more option arguments\n"
"try this to see the output:\n"
"./test -d aaa -u ccc 4 \"quoted multi token string\" -d -u\n");
}
int main(int argc, char **argv)
{
char data[101];
int opt;
memset(data, 0, 101);
while ((opt = getopt(argc, argv, "hd:u:t:")) != -1) {
switch (opt) {
case 'h':
usage();
return 0;
case 'd': // can accept 0 or 1 parameters
if (optarg[0] == '-') { //not an optarg of ours...
optind--;
printf("option: -d. no tokens (another option follows)\n");
break;
}
strncpy(data, optarg, 100);
printf("option: -d. tokens: %s\n", data);
break;
case 'u': //can accept one or more parameters ...
strncpy(data, optarg, 100);
printf("option: -u. tokens: %s", data);
//do we have more arguments for 'u'?
while( optind <= argc && argv[optind][0] != '-') {
strncpy(data, argv[optind], 100);
printf(", %s", data);
optind++;
}
printf(".\n");
break;
case ':': //this happens if we got an option which expects an arg without any optarg.
if(optopt == 'd') {//lets allow a '-d' without its optarg
printf("option: -d. no tokens\n");
break;
}
//otherwise fall through to the default handler
default: //covers ':' '?' for missing value, '-h' for help, etc.
printf("on error you get: opt=%c. optopt=%c opterr=%d\n", opt, optopt, opterr);
return 0;
}
}
return 0;
}
You can't do that using plain C code. You'll have to write platform specific assembly language code to handle that.
Your best option is to use the C proprocessor.
int main(int argc, char *argv[])
{
#include "myfile.txt"
// Do Stuff
}
Having said that, I don't know what you would gain by that instead of putting the contents of myfile.txt in main() directly.
I am working on trying to take in command line arguments. If I want to have multiple optional command line arguments how would I go about doing that? For example you can run the program in the following ways:
(a is required every instance but -b -c -d can be given optionally and in any order)
./myprogram -a
./myprogram -a -c -d
./myprogram -a -d -b
I know that getopt()'s third argument is options. I can set these options to be "abc" but will the way I have my switch case set up causes the loop to break at each option.
The order doesn't matter so far as getopt() is concerned. All that matters is your third argument to getopt() (ie: it's format string) is correct:
The follow format strings are all equivalent:
"c:ba"
"c:ab"
"ac:b"
"abc:"
In your particular case, the format string just needs to be something like "abcd", and the switch() statement is properly populated.
The following minimal example¹ will help.
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main (int argc, char **argv)
{
int aflag = 0;
int bflag = 0;
char *cvalue = NULL;
int index;
int c;
opterr = 0;
while ((c = getopt (argc, argv, "abc:")) != -1)
{
switch (c)
{
case 'a':
aflag = 1;
break;
case 'b':
bflag = 1;
break;
case 'c':
cvalue = optarg;
break;
case '?':
if (optopt == 'c')
fprintf (stderr, "Option -%c requires an argument.\n", optopt);
else if (isprint (optopt))
fprintf (stderr, "Unknown option `-%c'.\n", optopt);
else
fprintf (stderr,
"Unknown option character `\\x%x'.\n",
optopt);
return 1;
default:
abort ();
}
}
printf ("aflag = %d, bflag = %d, cvalue = %s\n",
aflag, bflag, cvalue);
for (index = optind; index < argc; index++)
printf ("Non-option argument %s\n", argv[index]);
return 0;
}
¹Example taken from the GNU manual
I wrote (copied and pasted from Google and simplified) a C program to use getopt to print out the values of the arguments passed in from the Unix command line.
From Unix command line:
./myprog -a 0 -b 1 -c 2
My C code is:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int i;
while ((i = getopt(argc, argv, "abc")) != -1) {
switch (i) {
case 'a':
printf("A = %s\n", optarg);
break;
case 'b':
printf("B = %s\n", optarg);
break;
case 'c':
printf("C = %s\n", optarg);
break;
default:
break;
}
}
return 0;
}
I want to program to print out each of the values passed e.g.
A = 0
B = 1
C = 2
However it is not printing out anything at all.
You forget about ":" after any option with argument. If you will change one line
while ((i = getopt(argc, argv, "a:b:c:")) != -1) {
you will get working variant.
Read properly man 3 getopt, it said about third argument of getopt that
… optstring is a string containing the legitimate option characters. If such a character is followed by a colon, the option requires an argument, so
getopt() places a pointer to the following text in the same argv-element, or the text of the following argv-element, in optarg. Two colons mean an option
takes an optional arg; if there is text in the current argv-element (i.e., in the same word as the option name itself, for example, "-oarg"), then it is
returned in optarg, otherwise optarg is set to zero. …
I need to use getopt to parse the following command:
./center -n name –cp Anumber –i Anumber –t Anumber –s Anumber -fc nameOfaFile
All of them can be given out in any order.
So its clear i have to use getOpt.
So i have been investigation and this is what i have so far
void check_parameters (int argc, char** argv) {
int opt;
while((opt = getopt(argc, argv, "n:cp:i:c:fc:")) != -1) {
printf("give me opt %c \n", opt)
swicth(opt){
case 'n' :
//do something
case 'cp' :
//do something
case 'i' :
//do something
}
}
}
I do know for a fact that the 3 parameter: the OptString is where i tell getOpt what are the char of the options it should expect
Also in optarg is where the argument is
its not working!
when i printf( "give me opt %c", opt ) its giving me the numbers, not the char!
for this reason it does not work. it does not check the parameters correctly
Edit:
After doing man 3 getopt
the return value of getopt is: If an option was successfully found, then getopt() returns the option character. If all command-line options have been parsed, then getopt() returns -1.
it returns the option character? it always return integer 1. always! i don't understand how getopt works. how does the switch compare to characters?!
First of all, this:
case 'cp':
...makes no sense. You can't represent two characters with a char or int. Besides, if running your program with -cp 101, getopt will treat it as -c p 101, for a completely different result. If you want to use long options, use getopt_long instead and the appropriate calling syntax, per example: ./a.out -n 42 --cp 101.
Now, here's a working example of getopt() in action:
#include <stdio.h>
#include <getopt.h>
int main(int argc, char* argv[]) {
int opt;
while((opt = getopt(argc, argv, "n:cp:i:c:fc:")) != -1) {
switch (opt){
case 'n':
printf("n was %s\n", optarg);
break;
case 'i':
printf("i was %s\n", optarg);
break;
}
}
return 0;
}
getopt handles only single-character option names, not things like -cp or -fc (which it interprets as multiple option flags in a single string: -c -p, -f -c). If you want to stick to the syntax you defined, then it's easier to just roll your own:
// first check whether argc is odd, then:
for (i = 1; i < argc; i += 2) {
if (strcmp(argv[i], "n") == 0) {
name = argv[i+1];
} else if (strcmp(argv[i], "cp") == 0) {
// etc. etc.
}
}