Parsing argv by hand gets old fast once you have more than two flags. getopt.h is a POSIX/GNU header that provides getopt for short options (-v, -o file) and getopt_long for long options (--verbose, --output=file). It handles the argument loop, the option string, and the optarg/optind bookkeeping.
#include <getopt.h> #include <stdio.h> int main(int argc, char *argv[]) { int verbose = 0; const char *outfile = NULL; int c; while ((c = getopt(argc, argv, "vo:")) != -1) { switch (c) { case 'v': verbose = 1; break; case 'o': outfile = optarg; break; // optarg points to the argument case '?': return 1; // unknown option, getopt printed an error } } // non-option arguments start at argv[optind] for (int i = optind; i < argc; i++) printf("input: %s\n", argv[i]); return 0; }
The option string "vo:" means: -v takes no argument; -o requires one (the colon). A leading colon in the string (":vo:") suppresses getopt's automatic error message and returns ':' for a missing argument instead, letting you handle errors yourself.
getopt_long adds long option support via a struct option array:
static struct option long_opts[] = { {"verbose", no_argument, NULL, 'v'}, {"output", required_argument, NULL, 'o'}, {"help", no_argument, NULL, 'h'}, {0, 0, 0, 0} // sentinel }; while ((c = getopt_long(argc, argv, "vo:h", long_opts, NULL)) != -1) { ... }
The third field in struct option can point to a flag variable to set instead of returning a character — useful for boolean flags you want to set directly. Long options with optional_argument require --opt=value syntax; --opt value is not recognised as providing an argument.
getopt permutes argv as it processes it, moving non-option arguments to the end (GNU behaviour). Setting POSIXLY_CORRECT in the environment, or using a leading + in the option string, stops processing at the first non-option argument instead.
// compile: gcc -o tool tool.c // run: ./tool -v --output=out.txt file1.txt file2.txt // description: parse a mix of short and long options #include <getopt.h> #include <stdio.h> int main(int argc, char *argv[]) { int verbose = 0; const char *outfile = "a.out"; int c; static struct option opts[] = { {"verbose", no_argument, NULL, 'v'}, {"output", required_argument, NULL, 'o'}, {0, 0, 0, 0} }; while ((c = getopt_long(argc, argv, "vo:", opts, NULL)) != -1) { switch (c) { case 'v': verbose = 1; break; case 'o': outfile = optarg; break; case '?': return 1; } } printf("verbose=%d output=%s\n", verbose, outfile); for (int i = optind; i < argc; i++) printf(" input: %s\n", argv[i]); return 0; }
Try ./tool --output out.txt file.txt (space-separated) — it works. Then try ./tool --output=out.txt file.txt (equals sign) — it also works. Now try optional_argument for a flag and compare: --opt value will not bind value as the argument, only --opt=value will.