getopt — Professional CLI Argument Parsing
`getopt` (POSIX) is the standard function for parsing command-line options in C programs. It handles `-v`, `-o file`, `-n 10` style arguments cleanly, with proper error reporting. Professional CLI tools (grep, gcc, ls) all use it. `getopt_long` additionally handles `--verbose`, `--output=file` GNU-style long options.
getopt & getopt_long — Complete CLI Parser
/* cli_tool.c — professional argument parsing with getopt */
#define _POSIX_C_SOURCE 200809L /* enable POSIX extensions */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h> /* POSIX: getopt, getopt_long */
/* Tool configuration from CLI */
typedef struct {
int verbose;
int recursive;
int count;
const char *output_file;
const char *input_file;
} Config;
static void usage(const char *prog) {
fprintf(stderr,
"Usage: %s [OPTIONS] <input_file>
"
"Options:
"
" -v, --verbose Enable verbose output
"
" -r, --recursive Process recursively
"
" -n, --count=N Process N items (default: 10)
"
" -o, --output=FILE Write output to FILE
"
" -h, --help Show this help
",
prog);
}
int parse_args(int argc, char **argv, Config *cfg) {
/* Initialise defaults */
cfg->verbose = 0;
cfg->recursive = 0;
cfg->count = 10;
cfg->output_file = NULL;
cfg->input_file = NULL;
/* ── getopt short options string ─────────────────────────
Each char = a short option.
':' after a char = requires an argument.
'::' = optional argument. */
const char *short_opts = "vrn:o:h";
/* ── getopt_long — long options table ────────────────────
{long_name, has_arg, flag, val}
has_arg: no_argument / required_argument / optional_argument
flag=NULL, val='v' → getopt_long returns 'v' (same as short) */
static struct option long_opts[] = {
{"verbose", no_argument, NULL, 'v'},
{"recursive", no_argument, NULL, 'r'},
{"count", required_argument, NULL, 'n'},
{"output", required_argument, NULL, 'o'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0} /* sentinel — always last */
};
int opt;
int long_index = 0;
/* getopt_long returns the option char, or -1 at end, or '?' on error */
while ((opt = getopt_long(argc, argv, short_opts,
long_opts, &long_index)) != -1) {
switch (opt) {
case 'v': cfg->verbose = 1; break;
case 'r': cfg->recursive = 1; break;
case 'n':
cfg->count = atoi(optarg); /* optarg: the option argument */
if (cfg->count <= 0) {
fprintf(stderr, "Error: -n must be positive
");
return -1;
}
break;
case 'o': cfg->output_file = optarg; break;
case 'h': usage(argv[0]); exit(0);
case '?':
/* getopt already printed an error message */
usage(argv[0]);
return -1;
default: return -1;
}
}
/* After getopt, optind = index of first non-option argument */
if (optind >= argc) {
fprintf(stderr, "Error: input file required
");
usage(argv[0]);
return -1;
}
cfg->input_file = argv[optind];
return 0;
}
int main(int argc, char **argv) {
Config cfg;
if (parse_args(argc, argv, &cfg) != 0) return 1;
printf("Running with:
");
printf(" input: %s
", cfg.input_file);
printf(" output: %s
", cfg.output_file ? cfg.output_file : "(stdout)");
printf(" count: %d
", cfg.count);
printf(" verbose: %s
", cfg.verbose ? "yes" : "no");
printf(" recurse: %s
", cfg.recursive ? "yes" : "no");
return 0;
}
/* Build: gcc -Wall -Wextra -std=c99 cli_tool.c -o cli_tool
Test: ./cli_tool -v -n 5 --output=result.txt input.txt
./cli_tool --verbose --count=20 --recursive data.txt */Common Mistakes
- `optarg` is only valid during that iteration — don't save the `optarg` pointer for later use after the loop. For string options, `strdup(optarg)` if you need to store it (remember to `free`).
- Not checking `optind` after the loop — `optind` tells you where non-option arguments start. After parsing all options, `argv[optind..argc-1]` are the positional arguments.
- Using `atoi` without range checking — `atoi` returns 0 on failure with no error. Use `strtol` with endptr for robust integer parsing: if `endptr == start` or remainder is non-numeric, the input is invalid.
Tip
Tip
Practice getopt Professional CLI Argument Parsing in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
Understanding memory layout is essential for efficient C programming
Practice Task
Note
Practice Task — (1) Write a working example of getopt Professional CLI Argument Parsing from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with getopt Professional CLI Argument Parsing is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready c code.
Key Takeaways
- `getopt` (POSIX) is the standard function for parsing command-line options in C programs.
- `optarg` is only valid during that iteration — don't save the `optarg` pointer for later use after the loop. For string options, `strdup(optarg)` if you need to store it (remember to `free`).
- Not checking `optind` after the loop — `optind` tells you where non-option arguments start. After parsing all options, `argv[optind..argc-1]` are the positional arguments.
- Using `atoi` without range checking — `atoi` returns 0 on failure with no error. Use `strtol` with endptr for robust integer parsing: if `endptr == start` or remainder is non-numeric, the input is invalid.