Use getopt(3) to parse CLI arguments

If you're building a program with a CLI you often want to provide some switches that allows the user to control the behaviour of the program.

I frequently define the following options -h (for help) and -v (for verbose). In my opinion, a program CLI should strive to make the behaviour of the program discoverable through its help functionality. Having a verbose flag is generally also useful.

Defining options in the option string has always seemed a little esoteric to me so here's a small translation table:

When an option takes an argument the argument is found in optarg.

If option parsing finishes and no errors are reported, the variable optind is an index in argv of the first positional argument (ie non-option).

The following is an example program that defines -h for help, -v for verbose, -o for an output file and requires an input file.

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

void usage(int status);

/* Capture the program name here */
static char *progname;

int
main(int argc, char *argv[])
{
	int verbose;
	int opt;
	char *input, *output;

	input = output = NULL;
	verbose = 0;
	progname = argv[0];

	while ((opt = getopt(argc, argv, "hvo:")) != -1) {
		switch (opt) {
		case 'v':
			verbose = 1;
			break;
		case 'h':
			usage(EXIT_SUCCESS);
		case 'o':
			output = optarg;
			break;
		default: /* '?' */
			usage(EXIT_FAILURE);
		}
	}

	if (optind >= argc) {
		fprintf(stderr, "Expected argument <name> after options.\n");
		exit(EXIT_FAILURE);
	}
	input = argv[optind];

	if (verbose) {
		fprintf(stderr, "input: %s\n", input);
		if (output)
			fprintf(stderr, "output: %s\n", output);
	}

	exit(EXIT_SUCCESS);
}

void
usage(int status)
{
	fprint(stderr, "usage: %s [-h] [-v] [-o <output>] <input>\n", progname);
	exit(status);
}

The above program can be built and executed with:

$ gcc -o opt main.c
$ ./opt -h
usage: ./opt [-h] [-v] [-o <output>] <input>
$ ./opt -v foo -o bar
input: foo
output: bar

There are some GNU extensions to be aware of and other quirks, but I only wanted to cover the basics here. You might have noticed that we did not cover defining long options like --help or --verbose. This is because they are GNU extensions! If you want more details, I recommend reading your man pages.

man 3 getopt