Parse Command Line Arguments With getopt In C

Getopt is a great function for parsing command line arguments, and a function that I did not learn about until I was looking at some Busybox C code. There are two versions of getopt, one for a long set of arguments and the basic version that I plan to outline here for use on a small set.

So how does getopt help you? What makes it different from just running through argv**? Well essentially it does the work for you! As a programmer it is always nice to do things easily and efficiently. The goal of this article is to familiarize you with the basic functionality of getopt in C.

The basic form of getopt is:

int getopt(int argc, char * const argv[],  const char *optstring);

Where int argc and char *const argv[] are just passed in from your int main(int argc, char **argv);. It should also be noted that getopt will set some external variables that are provided when you include unistd.h to use getopt, they are:

extern char *optarg;
extern int optind, opterr, optopt;

getopt returns an integer which you can compare as ASCII against values in a switch statement.

Using getopt()

So, now we know what getopt is, how do we use it? The main idea is to use it in a loop, once all the command line arguments have been parsed getopt will return a -1.

Okay, so we know what the first two arguments for getopt are (argc, and argv from your int main() )…but what about the third argument? Well that is where your flag definitions come in. So lets say you have a program that has three flags: { a, b, c } where its -c followed by a value. All you have to do is feed those values as the third argument, for example:

  getopt(argc, argv, "abc:");

Notice that it is c followed by a colon. The colon means that a value should follow the c and thus we should parse that value if we see the -c. One thing to note is that if getopt receives a character that is not part of optstring then getopt will return a question mark ( ? ) since we don’t know what value a user will put after the -c, we know we will receive a ? if they use that flag. This means in your switch statement you need a case for ? .

Example Program Using getopt()

Here is the program all put together, using a, b, c as our flags. In this example you can also see I used some of the global external variables that getopt automatically sets including optarg and optopt.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
 
int
main (int argc, char **argv)
{
  int adata = 0; // Flag a
  int bdata = 0; // Flag b
  char *cvalue = NULL; // The value argument after c
  int index;
  int c;
 
  opterr = 0;
 
  while ((c = getopt (argc, argv, "abc:")) != -1)
	switch (c){
	  case 'a':
		fprintf(stdout, "There is a -a.\n");
		break;
 
	  case 'b':
		fprintf(stdout, "There is a -b.\n");
		break;
 
	  case 'c':
		fprintf(stdout, "There is a -c and perhaps a value.\n");
		cvalue = optarg;
                fprintf(stdout, "C is: %s \n",cvalue);
		break;
 
	  case '?':
		// This is a case where they have either placed a -c with an argument.
		// OR there is a rogue character on the command line.
 
		if (optopt == 'c')
			fprintf (stderr, "A -c %c was specified but 
				not argument present. \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 ();
	  }
 
  // We can spit out the characters that are not part of the command line.
  for (index = optind; index < argc; index++)
	  printf ("Non-option argument %s\n", argv[index]);
  return 0;
}

Okay, now that the program is made, lets run it:

erik@debian:~/getopt_y$ gcc -o getopt_test getopt_test.c
erik@debian:~/getopt_y$ ./getopt_test -ab
There is a -a.
There is a -b.
C is: (null)
erik@debian:~/getopt_y$ ./getopt_test -abc
There is a -a.
There is a -b.
A -c c was specified but not argument present.
erik@debian:~/getopt_y$ ./getopt_test -abc yar
There is a -a.
There is a -b.
There is a -c .. and perhaps a value.
C is: yar
erik@debian:~/getopt_y$ ./getopt_test -a
There is a -a.
C is: (null)
erik@debian:~/getopt_y$ ./getopt_test -b
There is a -b.
C is: (null)
erik@debian:~/getopt_y$ ./getopt_test -c
A -c c was specified but not argument present.

So there you have it, a simple and effective way to parse your command line arguments rather than using strcmp to compare each value on its own.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *