HOWTO · C++

Manejar argumentos usando getopt en C++

Este artículo demuestra cómo usar la función getopt() en C++ para manejar argumentos.

En esta página

Para analizar opciones cortas de línea de comandos en C++, llama a la función POSIX getopt con argc, argv y un optstring que indique cada opción y si requiere un valor. Usa optarg para obtener los valores de las opciones y optind para acceder a los argumentos posicionales restantes. Si el programa también necesita opciones largas, como --help, usa getopt_long.

Cómo funciona getopt en C++

POSIX especifica getopt, una función disponible habitualmente en sistemas similares a Unix; no forma parte de la biblioteca estándar de C++. En los sistemas que lo proporcionan, incluye <unistd.h>. La función lee argc y argv y devuelve una opción analizada cada vez. Una opción corta consta de un solo carácter, como -v; también puede ir seguida de un valor, como en -o report.txt. getopt solo analiza opciones cortas, por lo que un elemento como --output requiere otra interfaz. Consulta la especificación POSIX de getopt para conocer su interfaz y las reglas de sus valores de retorno.

La disponibilidad y algunos detalles, como el orden de los argumentos, dependen de la plataforma. Si la portabilidad es importante, consulta los encabezados y la documentación del sistema de destino. Los ejemplos siguientes usan interfaces POSIX y un compilador de C++, como g++.

Definir opciones cortas y analizar argumentos

Un optstring enumera las opciones aceptadas de un solo carácter. Si un carácter va seguido de :, esa opción requiere un valor, que getopt proporciona mediante optarg. En el ejemplo, v es una opción de tipo indicador y o: acepta una ruta de salida. El programa también imprime el valor posicional que queda después del análisis. Guarda el código como main.cpp; en un sistema similar a POSIX, compílalo con g++ -std=c++17 -Wall -Wextra -pedantic main.cpp -o app.

Ejecuta este ejemplo:

El código y las etiquetas o salidas visibles de esta sección se mantienen en inglés.

#include <iostream>
#include <unistd.h>

int main() {
    char app[] = "app";
    char verbose_option[] = "-v";
    char output_option[] = "-o";
    char output_path[] = "report.txt";
    char input_path[] = "input.csv";
    char* arguments[] = {app, verbose_option, output_option, output_path, input_path, nullptr};
    int argument_count = 5;
    bool verbose = false;
    const char* output = "result.txt";
    int option = 0;

    opterr = 0;
    while ((option = getopt(argument_count, arguments, ":vo:")) != -1) {
        switch (option) {
            case 'v':
                verbose = true;
                break;
            case 'o':
                output = optarg;
                break;
            case ':':
                std::cerr << "Option -" << static_cast<char>(optopt) << " requires a value\n";
                return 2;
            case '?':
            default:
                std::cerr << "Unknown option: -" << static_cast<char>(optopt) << '\n';
                return 2;
        }
    }

    std::cout << "verbose=" << (verbose ? "on" : "off") << '\n';
    std::cout << "output=" << output << '\n';
    for (int i = optind; i < argument_count; ++i) {
        std::cout << "input=" << arguments[i] << '\n';
    }
}
verbose=on
output=report.txt
input=input.csv

El arreglo fijo de argumentos hace que el ejemplo sea autónomo; en un programa normal, pasa los valores argc y argv que recibe main. El optstring :vo: empieza con dos puntos para distinguir la ausencia de un valor obligatorio de una opción desconocida: v no lleva valor y o: sí lo requiere. Normalmente se aceptan tanto -o report.txt como -oreport.txt. optarg apunta al valor asociado con la opción que se está procesando.

Gestionar errores y argumentos restantes

Normalmente, getopt devuelve -1 cuando termina de analizar las opciones. Devuelve ? para una opción desconocida y también cuando falta el valor de una opción obligatoria, salvo que el optstring comience con :. Con esa convención, devuelve : si falta el valor y sigue devolviendo ? para una opción desconocida. optopt identifica el carácter de opción implicado. El ejemplo de opciones cortas usa esta convención y establece opterr en 0; así, su switch muestra un único mensaje de error y devuelve un estado distinto de cero en cualquiera de los dos casos. Si se omite los dos puntos iniciales, ambas condiciones producen ? y quien llama a la función no puede distinguirlas solo por el valor de retorno. El mismo ajuste de opterr suprime el mensaje de error de la biblioteca.

Después del análisis, optind contiene el índice del primer argumento restante. Recorre los argumentos desde optind hasta argc para procesar nombres de archivo u otros valores posicionales. Algunas implementaciones reordenan las opciones y los operandos durante el análisis; otras se detienen ante el primer operando o permiten controlar el orden de otra forma. No des por sentado que todas las plataformas procesan igual las opciones intercaladas con argumentos posicionales.

Usar getopt_long para opciones largas

En los sistemas que proporcionan la extensión GNU <getopt.h>, getopt_long reconoce nombres largos además de opciones cortas; no está especificado por POSIX ni forma parte de la biblioteca estándar de C++. El manual de la biblioteca GNU C sobre getopt_long describe esta extensión y su tabla struct option. Cada entrada indica un nombre largo, si acepta ningún valor, uno obligatorio u uno opcional, y el valor que se devuelve al switch. Una entrada final con nombre nulo marca el fin de la tabla. Esta demostración autónoma acepta --help y --output FILE, además de sus formas cortas:

El código y las etiquetas o salidas visibles de esta sección se mantienen en inglés.

#include <getopt.h>
#include <iostream>

int main() {
    char app[] = "app";
    char help_option[] = "--help";
    char* arguments[] = {app, help_option, nullptr};
    int argument_count = 2;
    const option long_options[] = {
        {"help", no_argument, nullptr, 'h'},
        {"output", required_argument, nullptr, 'o'},
        {nullptr, 0, nullptr, 0}
    };
    const char* output = "result.txt";
    int option = 0;

    opterr = 0;
    while ((option = getopt_long(argument_count, arguments, ":ho:", long_options, nullptr)) != -1) {
        switch (option) {
            case 'h':
                std::cout << "Usage: app [-h] [-o FILE] [FILE...]\n";
                break;
            case 'o':
                output = optarg;
                break;
            case ':':
                std::cerr << "Option requires a value\n";
                return 2;
            case '?':
            default:
                std::cerr << "Unknown option\n";
                return 2;
        }
    }

    std::cout << "output=" << output << '\n';
    for (int i = optind; i < argument_count; ++i) {
        std::cout << "input=" << arguments[i] << '\n';
    }
}
Usage: app [-h] [-o FILE] [FILE...]
output=result.txt

En un programa de línea de comandos real, usa los valores argc y argv de main y compila con g++ -std=c++17 -Wall -Wextra -pedantic main.cpp -o app en un sistema que proporcione getopt_long. La entrada fija del ejemplo selecciona la ruta de ayuda; sustitúyela por --output report.txt input.csv para elegir una ruta de salida y dejar input.csv disponible para procesarlo mediante optind. Comprueba la plataforma de destino antes de depender de esta extensión.