C Command-Line Tools with getopt: Parse Arguments Like a Linux Utility

Command-line programs are an important part of Linux and Unix-like systems. C provides argc and argv for basic argument processing, while the POSIX getopt function makes it easier to handle short command-line options such as -v, -f file, or -o output.txt.

What Are Command-Line Arguments?

Command-line arguments are values supplied to a program when it is started from a terminal. The main function receives them through argc and argv.

C
#include <stdio.h>

int main(int argc, char *argv[])
{
    printf("Argument count: %d\n", argc);

    for (int i = 0; i < argc; i++)
    {
        printf("argv[%d] = %s\n", i, argv[i]);
    }

    return 0;
}

Understanding argc and argv

ItemMeaning
argcNumber of command-line arguments
argvArray of argument strings
argv[0]Usually the program name or invocation path
argv[1] and laterArguments supplied by the user

A Simple Command-Line Program

Suppose a program named greet accepts a name from the command line.

C
#include <stdio.h>

int main(int argc, char *argv[])
{
    if (argc < 2)
    {
        printf("Usage: %s <name>\n", argv[0]);
        return 1;
    }

    printf("Hello, %s!\n", argv[1]);

    return 0;
}

After compiling, a command such as ./greet Alice supplies Alice as argv[1].

Why Use getopt?

Manually processing argv becomes cumbersome when a program supports multiple options. getopt provides a standard POSIX interface for parsing short options such as -v, -n, -o filename, and combined options such as -abc.

Basic getopt Example

The following program supports -v for verbose output and -h for help.

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

int main(int argc, char *argv[])
{
    int option;

    while ((option = getopt(argc, argv, "vh")) != -1)
    {
        switch (option)
        {
            case 'v':
                printf("Verbose mode enabled\n");
                break;

            case 'h':
                printf("Usage: %s [-v] [-h]\n", argv[0]);
                return 0;

            default:
                fprintf(stderr, "Usage: %s [-v] [-h]\n", argv[0]);
                return 1;
        }
    }

    return 0;
}

The getopt Option String

The third argument to getopt describes which short options the program accepts. A letter by itself represents a flag. A letter followed by a colon requires an argument.

Option StringMeaning
vAccept -v without an argument
o:Accept -o followed by a required argument
vhAccept -v and -h
v:o:Accept -v with an optional argument only where supported by the implementation; o requires an argument

Options with Arguments

When an option requires an argument, getopt makes that value available through the global variable optarg.

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

int main(int argc, char *argv[])
{
    int option;

    while ((option = getopt(argc, argv, "o:")) != -1)
    {
        switch (option)
        {
            case 'o':
                printf("Output file: %s\n", optarg);
                break;

            default:
                fprintf(stderr, "Usage: %s [-o file]\n", argv[0]);
                return 1;
        }
    }

    return 0;
}

Using optind for Remaining Arguments

After getopt finishes processing options, optind points to the first argument that was not processed as an option.

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

int main(int argc, char *argv[])
{
    int option;

    while ((option = getopt(argc, argv, "v")) != -1)
    {
        switch (option)
        {
            case 'v':
                printf("Verbose mode\n");
                break;

            default:
                return 1;
        }
    }

    for (int i = optind; i < argc; i++)
    {
        printf("Input: %s\n", argv[i]);
    }

    return 0;
}

Handling Missing Option Arguments

If an option requires an argument but none is supplied, getopt reports an error. Programs should handle this case and display useful usage information.

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

int main(int argc, char *argv[])
{
    int option;

    while ((option = getopt(argc, argv, "o:")) != -1)
    {
        switch (option)
        {
            case 'o':
                printf("Output: %s\n", optarg);
                break;

            case '?':
                fprintf(stderr, "Usage: %s [-o file]\n", argv[0]);
                return 1;
        }
    }

    return 0;
}

The ? Return Value

getopt normally returns '?' when it encounters an unknown option or a required option argument is missing, depending on the option-string configuration and implementation.

Suppressing getopt Error Messages

Setting opterr to zero prevents getopt from automatically printing its own diagnostic messages. This allows the application to produce its own consistent error messages.

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

int main(int argc, char *argv[])
{
    int option;

    opterr = 0;

    while ((option = getopt(argc, argv, "o:")) != -1)
    {
        if (option == '?')
        {
            fprintf(stderr, "Invalid option or missing argument.\n");
            return 1;
        }
    }

    return 0;
}

A Practical CLI Tool

A useful command-line utility might support verbose mode, an output file, and a help option.

C
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>

static void printUsage(const char *program)
{
    printf("Usage: %s [-v] [-o output] [input ...]\n", program);
    printf("  -v          Enable verbose mode\n");
    printf("  -o FILE     Select output file\n");
    printf("  -h          Show this help message\n");
}

int main(int argc, char *argv[])
{
    bool verbose = false;
    const char *output = NULL;
    int option;

    while ((option = getopt(argc, argv, "vo:h")) != -1)
    {
        switch (option)
        {
            case 'v':
                verbose = true;
                break;

            case 'o':
                output = optarg;
                break;

            case 'h':
                printUsage(argv[0]);
                return 0;

            case '?':
                printUsage(argv[0]);
                return 1;
        }
    }

    if (verbose)
    {
        printf("Verbose mode enabled\n");
    }

    if (output != NULL)
    {
        printf("Output: %s\n", output);
    }

    for (int i = optind; i < argc; i++)
    {
        printf("Input: %s\n", argv[i]);
    }

    return 0;
}

Short Options Can Be Combined

Short options that do not require arguments can often be combined. For example, -vh can represent -v -h.

TEXT
./tool -vh

# Equivalent to
./tool -v -h

Using -- to End Option Processing

The -- marker is commonly used to tell command-line parsers that following arguments should be treated as operands rather than options. This is particularly useful when a filename begins with a hyphen.

TEXT
./tool -- -input.txt

After option parsing stops, the remaining operands can be processed using argv starting at optind.

Parsing Numeric Arguments

Command-line option arguments arrive as strings. Use functions such as strtol to convert numeric input while also validating errors.

C
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

static int parseInt(const char *text, int *result)
{
    char *end;
    long value;

    errno = 0;
    value = strtol(text, &end, 10);

    if (errno != 0 || end == text || *end != '\0' ||
        value < INT_MIN || value > INT_MAX)
    {
        return 0;
    }

    *result = (int)value;
    return 1;
}

A Tool with a Numeric Option

C
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int count = 1;
    int option;

    while ((option = getopt(argc, argv, "n:")) != -1)
    {
        switch (option)
        {
            case 'n':
            {
                char *end;
                long value;

                errno = 0;
                value = strtol(optarg, &end, 10);

                if (errno != 0 || end == optarg || *end != '\0' ||
                    value < 1 || value > INT_MAX)
                {
                    fprintf(stderr, "Invalid count: %s\n", optarg);
                    return 1;
                }

                count = (int)value;
                break;
            }

            default:
                fprintf(stderr, "Usage: %s [-n count]\n", argv[0]);
                return 1;
        }
    }

    printf("Count: %d\n", count);

    return 0;
}

Building and Running a getopt Program

TEXT
cc -std=c17 -Wall -Wextra -Wpedantic tool.c -o tool

./tool -v -o result.txt input.txt
./tool -vh
./tool -- -input.txt

getopt on Linux and POSIX Systems

getopt is a POSIX interface and is widely available on Linux and other Unix-like systems. It is not part of the ISO C standard itself. Code intended to be strictly portable across all C implementations should account for this distinction.

getopt vs Manual argv Parsing

ApproachBest For
argc / argvVery small programs with simple arguments
getoptUnix/Linux tools using short options
Custom parserSpecialized command-line grammars
Third-party CLI libraryLarge applications with advanced option systems

Common Mistakes

  • Forgetting to include unistd.h on POSIX systems
  • Failing to handle unknown options
  • Not checking for missing required option arguments
  • Treating optarg as a numeric value without validating the conversion
  • Forgetting that option arguments are strings
  • Processing remaining operands without using optind
  • Assuming getopt is part of ISO C

Best Practices

  • Provide a clear usage message
  • Handle unknown and malformed options explicitly
  • Validate option arguments before using them
  • Use strtol instead of atoi when robust numeric validation is required
  • Keep command-line parsing separate from the program's core logic
  • Use -- when filenames or operands may begin with a hyphen
  • Return a nonzero exit status for command-line errors

Real-World Applications

  • Linux administration utilities
  • File-processing tools
  • Build and deployment utilities
  • System monitoring programs
  • Backup tools
  • Command-line data converters
  • Developer utilities

Practice Exercises

  • Create a program with -v and -h options
  • Add an -o option that accepts an output filename
  • Add a numeric -n option and validate its value
  • Process multiple input filenames after the options
  • Support combined short options such as -vh
  • Handle an input filename beginning with a hyphen using --
  • Build a small Linux-style command-line utility with a complete usage message

Conclusion

Command-line argument parsing is a fundamental skill for Linux C programming. argc and argv provide the basic building blocks, while getopt makes short-option parsing cleaner and more reliable. By combining getopt with validation, clear usage messages, and proper error handling, you can build C utilities that behave like professional command-line tools.

Note: Note: getopt is a POSIX API rather than an ISO C standard function. The examples in this article are intended primarily for Linux and other POSIX-compatible systems.