getopt to take only one argument - c

I have a question about getopt in C. I have multiple option flags and am trying to get it to take one argument for everything. The command line will look something like command -a fileName or command -b fileName or command -ab fileName. Basically every command takes in one fileName and if they want to combine commands they should only have to type in one fileName. In getopt the string looks like a:b: and a variable is set to argv[argc -1]. This is fine if it's just one option but fails if there are multiple options (ie command -ab fileName) since : forces users to input an option but :: will make the singe options not force the user to type in an option. Any suggestions?

The optind global variable lets you know how many of the argv strings were used. So one way to approach this problem is to just drop the colons, and use a string like "ab".
Here's an example of what the code looks like (adapted from the example in the man page):
int main(int argc, char *argv[])
{
int ch;
while ((ch = getopt(argc, argv, "ab")) != -1)
{
if (ch == 'a')
printf("Got A\n");
else if (ch == 'b')
printf("Got B\n");
else
printf("Got confused\n");
}
if (optind != argc-1)
printf("You forgot to enter the filename\n");
else
printf("File: %s\n", argv[optind]);
}
If you run this with a command line like
./test -ab hello
the output is
Got A
Got B
File: hello

Related

clarification on argc and argv

I have some doubts on what are argc and argv, i cant seem to grasp the concept, for what should i use them and how should i use them?
like i have this program that receives from the command line two integers between -100000 and 100000 computes thir addition and prints the result, while performing all needed check about te number of paramenters and their correctness.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int a, b;
char ch;
if (argc != 4)
{
printf("ERROR - Wrong number of command line parameters.\n");
exit(1);
}
if (sscanf(argv[1], "%d", &a) != 1)
{
printf("ERROR - the first parameter (%s) is not a valid integer.\n",
argv[1]);
exit(2);
}
if (sscanf(argv[2], "%d", &b) != 1)
{
printf("ERROR - the second parameter (%s) is not a valid integer.\n",
argv[2]);
exit(2);
}
ch = argv[3][0];
if (ch == 'a')
printf("The sum result is %d\n", a + b);
else if (ch == 'b')
printf("The subtraction result is %d\n", a - b);
else if (ch == 'c')
printf("The multiplication result is %d\n", a * b);
else if (ch == 'd')
{
if (b != 0)
printf("The division result is %d\n", a / b);
else
printf("ERROR the second value shoulb be different than 0 \n");
}
else
printf("ERROR parameter (%c) does not correspond to a valid value.\n",
ch);
return 0;
}
but how does the program receive from the command line two arguments?? where do i input them?? i am using codeblocks.
argc is the number of parameters passed to your program when it's invoked from command line.
argv is the array of received parameters, and it is an array of strings.
Note that the name of the program is always passed automatically.
Assuming your program executable is test, when you invoke from terminal:
./text 145 643
argc will be 3: program name and the two numbers
argv will be the char* array {"./text","145","643"}
When you write a code, say hello.c, you can run it from the terminal, by going to that directory/folder from the terminal, and compile it using a compiler like gcc.
gcc hello.c -o hello
If you are using Windows, with a compiler like Turbo C or Visual Studio, then it would create a .exe file. This creates an executable file.
When you run the file from command line, you can give command-line arguments as a way of input to the program.
On terminal, you could ./hello arg1 arg2, where arg1 and arg2 are the command line arguments to it. To see how to do this in Windows using a compiler like Turbo C, see this link too.
So what are argc and argv[]?
Your main functions uses main(int argc, char *argv[]), to take the command line arguments.
argc is the number of command line arguments passed. In the above case, that is 3.
argv[] is an array of strings, which in this case are 3 strings. argv[1] will be equal to "arg1" and argv[2] will be equal to "arg2". "./hello" will be in argv[0].
So you give your command line arguments in the command line, be it Linux or Windows. The above explanation was more for Linux. See this and this for command line arguments in Turbo C (I do not recommend Turbo C), and this in case of Visual C.
To know more about command line arguments, you can read this.

Program isn't creating file using name specified on the command line

For this program I am supposed to either write my name to the terminal or to the output file given by the user(case 'f').
#include <stdio.h>
int main(int argc, char **argv)
{
// no output file print to screen, print name to terminal
if (argc < 2)
{
fprintf(stdout, "name\n");
}
//print name to output file given by user
else
{
int option;
int fFlag = 0;
while ((option = getopt(argc, argv, "f:")) != -1)
{
//if case 'f' print to output file
switch (option)
{
case 'f':
fFlag = 1; // flag indicates writing name to file
break;
//case f or error
case '?':
printf("error");
break;
}
}
//write to name to output file
if (fFlag)
{
FILE *file = fopen(argv[1], "w");
fprintf(file, "name");
}
}
return 0;
}
My code works when I want to write my name to the terminal, but it doesn't work when I want to write my name to the file given by the user. The code compiles and runs, but the file just doesn't exist.
The file in the command line might not already exist. Am I supposed to create the file in the program?
I understand what I was doing wrong. Thank you to everyone!
In addition to what the commenters have said, you are using getopt for part of your parsing so should use it for the rest!
int main...
char *filename; /**1**/
...
case 'f':
fFlag = 1;
filename = optarg; /**2**/
....
FILE *file = fopen(filename , "w"); /**3**/
Explanation: the f: to getopt means that getopt is going to try to find the option that goes along with the f. In the getopt loop, case 'f', that option is put in the variable optarg. At least for GNU getopt, that is a pointer into argv, so you don't have to copy it. You can just stash its pointer value (at *2* above) into a variable you have created for the purpose (*1*). Then you can open that particular file without regard to where it is in argv (*3*). See the treatment of option c in the GNU getopt example.
Edit You haven't shown the command line you're using, but I am guessing it's something like
./foo -f my-output-file.txt
— in which case argv[1] is -f and argv[2] is my-output-file.txt. (argv[0] is the name of your executable.) Hence fopen(argv[1], ...) is not what you want ;) .

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.

Combine options with arguments getopt

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.

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