53 KiB
Capítulo 13 — getopt_long: el analizador de opciones, explicado bien
Capítulo intermedio, pero escrito para que se entienda de verdad. Se apoya en variables (
02), operadores (03),ify bucles (04), funciones (05) y cadenas (06).Es el capítulo más práctico del libro: aprendés una sola función (
getopt_long) y la domas hasta que no te encuentres nunca con una herramienta de consola que no sepas interpretar. En el siguiente capítulo (14-entrada-salida-estandar) vas a profundizar en las tres tuberías (stdin/stdout/stderr), que acá solo necesitás para mostrar los resultados.
Cómo leer este capítulo
Hasta ahora, todos tus programas preguntan mientras corren: te muestran un cartel y esperan a que
escribas algo por teclado. Las herramientas serias --ls, grep, git, gcc-- no hacen eso: les
pasás las órdenes cuando las ejecutás.
Este capítulo te enseña una sola función para hacerlo: getopt_long(). No tres funciones, no
diez. Una. Pero la vas a entender tan bien que podrás leer el código de cualquier herramienta y saber
exactamente qué hace cada cosa.
Está dividido en ocho partes. Después de cada concepto, una práctica --uno o más ejercicios cortos-- para que lo uses ya mismo. No saltees las prácticas: son la mitad del capítulo.
| Parte | De qué habla | La idea en una frase |
|---|---|---|
| 1 | El problema de parsear a mano | Escribir un parser de opciones desde cero es más trabajo del que parece. |
| 2 | La llamada a getopt_long |
La firma completa, parámetro por parámetro. |
| 3 | Los tres macros | no_argument, required_argument y optional_argument. |
| 4 | El struct de opciones largas | struct option: la tabla que conecta nombres largos con códigos cortos. |
| 5 | Las variables globales | optarg, optind, optopt: lo que getopt_long deja atrás para que las uses. |
| 6 | Errores y el caso '?' |
Qué pasa cuando algo sale mal y cómo manejarlo con estilo. |
| 7 | Posicionales después de optind |
Cómo recuperar los argumentos "de verdad" una vez terminada la magia. |
| 7.5 | La ayuda de uso | Un formato estándar para guiar al usuario (sin función en C, pero con un helper). |
| 8 | El ejercicio jefe | Juntás todo en una herramienta de consola completa. |
Los ejercicios se numeran en orden: 13.1, 13.2, 13.3, y así. Cada uno trae su escenario de
prueba (qué entra y qué tiene que salir) para que puedas comprobar tu solución.
Regla de oro del libro: el capítulo explica y muestra cómo funciona cada cosa, pero los ejercicios no traen la solución. El enunciado te da el problema; la respuesta la escribís vos.
PARTE 1 -- El problema de parsear a mano
1.1 El parser manual: tu primer intento (y por qué dejarlo)
Imaginate que querés hacer un programa al que se le puedan pasar opciones como estas:
./miapp --help -v --max=42 archivo.txt
Tu primer impulso es escribir algo así:
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--help") == 0) { /* ... */ }
else if (strcmp(argv[i], "-v") == 0) { /* ... */ }
else if (strncmp(argv[i], "--max=", 6) == 0) { /* ... */ }
}
Esto funciona para tres opciones. Pero mirá lo que viene:
- ¿Qué pasa si
-vpuede ir con valor (-v 5) y sin él (-v)? - ¿Cómo hacés para soportar opciones apiladas (
-abc=-a -b -c)? - ¿Y si el usuario escribe
--max=abc,--MAX,--max, o simplemente--maxal final? - ¿Dónde separás las opciones de los archivos posicionales?
Cada opción nueva duplica tu código. Cada caso de error lo escribís a mano. Es un trabajo de hormiga que ya alguien hizo por vos.
Para guardar. Esto no es una crítica a tu capacidad: es un reconocimiento a que
getopt_longfue escrito por personas que saben que repetimos el mismo patrón en cada programa. Delegatemos lo aburrido.
1.2 La alternativa: delegar en getopt_long
getopt_long() es una función estándar POSIX que recibe los argumentos de main, busca las opciones
que vos le pediste, y te devuelve una por una hasta agotarlas. Vos solo escribís un switch con
lo que querés hacer con cada opción; el resto lo resuelve la función:
- opciones apiladas (
-abc) - valores pegados (
--max=42) y separados (--max 42) - detección de opciones desconocidas
- aviso de valores faltantes
- reordenamiento interno (deja los posicionales juntos al final)
Todo eso, en una sola llamada. La contraparte corta (getopt) solo entiende -x. getopt_long
entiende tanto -x como --opcion-larga. Eso es todo lo que importa para este capítulo.
Práctica de la Parte 1
Ejercicio 13.1 -- Contar líneas de código manuales
Conceptos · parseo manual, comparación de cadenas, complejidad
Contexto · Antes de ver la solución, necesitás sentir cuánto trabajo evita getopt_long.
Simplificación: no escribís nada. Solo calculás.
Consigna · Dado este conjunto de opciones, ¿cuántas comparaciones individuales (strcmp o
strncmp) tendrías que escribir para reconocerlas todas con el método manual?
| Opción | Requiere valor? | Formas aceptadas |
|---|---|---|
-h / --help |
no | -h, --help |
-v / --verbose |
no | -v, --verbose |
-o / --output |
sí | -o f, -of, --output=f, --output f |
-m / --max |
sí | -m n, -mn, --max=n, --max n |
Contá cada comparación individual que tendrías que escribir en tu if/else. No uses getopt_long.
Solo pensá.
Entrada · Ninguna (ejercicio de análisis). Salida · El número total de comparaciones y una lista breve de por qué son necesarias.
Criterios de aceptación
- Contaste cada forma individual, no cada opción.
- Consideraste pegado con
=y separado por espacio como formas distintas. - Reconociste que
-ofnecesita recorrer carácter por carácter.
PARTE 2 -- La llamada a getopt_long
2.1 La firma completa
Acá está la función. Memorizá la forma; después desarmamos cada pieza.
int getopt_long(int argc,
char *const argv[],
const char *optstring,
const struct option *longopts,
int *longindex);
Pero en la práctica casi siempre la llamás así:
int opt;
int option_index = 0;
while ((opt = getopt_long(argc, argv, "hvo:m:",
long_options, &option_index)) != -1) {
switch (opt) {
case 'h': /* -h o --help */ break;
case 'v': /* -v o --verbose */ break;
case 'o': /* -o o --output */ break;
case 'm': /* -m o --max */ break;
case '?': /* error */ break;
}
}
Esa es toda la estructura. Una sola vez declarás las opciones (en long_options y en el
optstring). Después, el bucle se repite automáticamente hasta que devuelve -1, y en cada vuelta te
dice cuál opción encontró a través de opt.
2.2 Desglose parámetro por par
| Parámetro | Qué es | Qué le pasás | Por qué importa |
|---|---|---|---|
argc |
Cantidad de argumentos | El mismo argc de main |
Necesita la lista cruda |
argv |
Arreglo de cadenas | El mismo argv de main |
Recorre los argumentos por vos |
optstring |
Opciones cortas (ver Parte 3) | "hvo:m:" |
Dice cuáles son cortas y cuáles llevan valor |
longopts |
Tabla de opciones largas (ver Parte 4) | Nombre de tu array de struct option |
Dice qué nombres largos reconoce |
longindex |
Puntero a int (opcional, podés poner NULL) |
&option_index |
Si lo pasás, te dice en qué posición de la tabla encontró la opción |
El retorno de cada llamada:
- Un
charconvertido aint: el código de la opción encontrada ('h','v','o','m'). '?'si la opción es desconocida o le falta un valor requerido.-1cuando ya no quedan opciones: el bucle se rompe.
Ojo con esto. El retorno es
int(nochar) porque necesitás representar-1junto con los códigos de opción. Si lo hubieran devuelto comochar, en muchos sistemas-1y'ÿ'sería el mismo valor, y no podrías distinguirlos. Por eso la variable se llamaopt(unint) y nuncachar opt.
2.3 El patrón que se repite siempre
Cada programa que use getopt_long sigue exactamente el mismo esqueleto:
#include <getopt.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
int opt;
int option_index = 0;
/* Declará las opciones largas ANTES del bucle. */
static struct option long_options[] = { ... };
/* Bucle: getopt_long devuelve la opción en cada vuelta. */
while ((opt = getopt_long(argc, argv, "hvo:m:",
long_options, &option_index)) != -1) {
switch (opt) {
case 'h': break; /* una opción */
case 'v': break; /* otra opción */
case '?': break; /* error */
}
}
return 0;
}
Fijate que el bucle y el switch son siempre iguales. Lo único que cambia entre un programa y otro es:
- El contenido de
long_options(qué opciones largas reconocés). - El string de opciones cortas (el tercer argumento de
getopt_long). - Los cuerpos de los
case.
Momento ajá. Una vez que dominás este esqueleto, podés copiarlo a cualquier proyecto y solo cambiar las opciones. Es como un molde: la forma no cambia, solo el contenido.
Práctica de la Parte 2
Ejercicio 13.2 -- Leer la firma sin compilar
Conceptos · parámetros de getopt_long, tipos
Contexto · Antes de correr código, necesitás saber qué recibe y qué devuelve. Simplificación:
interpretar una llamada a getopt_long.
Consigna · Dada esta llamada:
while ((opt = getopt_long(argc, argv, "hvfo:", my_options, NULL)) != -1) { ... }
respondé sin compilar:
- ¿Cuántas opciones cortas se están declarando? ¿Cuáles son?
- ¿Cuáles llevan valor (y cuáles no)? Mirá el
optstring. - ¿Se están usando las opciones largas? ¿Cómo lo sabés?
- ¿Qué pasa si le pasás
NULLcomo último parámetro (longindex)? ¿Ocurre algo malo?
Entrada · La línea de llamada mostrada arriba. Salida · Las cuatro respuestas.
Criterios de aceptación
- Identificás las opciones cortas en el string.
- Distinguís cuáles necesitan valor por el
:siguiente. - Sabés que
NULLenlongindexes válido: simplemente no se llena la información del índice. - El
my_optionses un array destruct option, aunque no lo veas declarado acá.
Ejercicio 13.3 -- Completar el esqueleto
Conceptos · estructura básica, variables globales necesarias
Contexto · Vos tenés que escribir las variables que faltan para que el esqueleto compile (aunque
no haga nada). Simplificación: declarar lo mínimo para compilar un programa con getopt_long.
Consigna · Escribí la parte principal de un programa C (desde main hasta el cierre) que llame a
getopt_long con estas opciones:
- Cortas:
-h,-v,-d <archivo> - Largas:
--help,--verbose,--debug-file(esta última requiere un valor como archivo)
El cuerpo de cada case puede estar vacío. Lo importante es que las declaraciones sean correctas.
Criterios de aceptación
- Se incluye
<getopt.h>. - El
optstringcorto es"hvd:". - El array de
struct optiontiene filas parahelp,verboseydebug-file. - Cada fila larga declara correctamente si lleva o no argumento.
- El bucle es
while ((opt = getopt_long(...)) != -1).
PARTE 3 -- Los tres macros que definen "si lleva valor"
3.1 ¿Qué significa "argumento" en este contexto?
Antes de leer la tabla, una aclaración importante: cuando getopt_long habla de argumento, no se
refiere a los argumentos de main (argc/argv). Se refiere a si la opción lleva consigo un valor
adicional.
-v→ no lleva nada. Es un interruptor: prende o apaga algo.-d archivo.txt→ sí lleva un valor adicional:"archivo.txt".
Ese "sí" o "no" es exactamente lo que dicen los macros de esta parte.
3.2 Los tres valores posibles
Cada fila de tu tabla de struct option necesita una respuesta a esta pregunta: ¿la opción larga
lleva valor? Las opciones son tres:
| Macro | Significa | Ejemplo | ¿Se usa para? |
|---|---|---|---|
no_argument |
La opción no lleva valor (es un flag) | --help, -v |
Interruptores booleanos |
required_argument |
La opción sí lleva valor | --output=archivo, -d archivo |
Valores obligatorios |
optional_argument |
La opción puede llevar valor, pero no es obligatorio | --mode=fast (ó simplemente --mode) |
Valores opcionales con sentido por defecto |
Solo estos tres. No hay un cuarto. Si una opción lleva algo adicional, es uno de los dos primeros. Si puede ir sin nada, es el tercero.
Analogía. Pensá en las opciones como interruptores de luz:
no_argument→ un interruptor normal: prendé/apagá, punto.required_argument→ un dimmer (regulador): necesitás decir hasta dónde querés bajar la luz.optional_argument→ un dimmer con preset: si lo tocás solo prende al nivel medio; si lo girás, elegís el nivel vos.
3.3 El truco de los nombres
Los macros se llaman no_argument, required_argument y optional_argument. Notá que argument
lleva una "u" --es del inglés. En el contexto de esta función, "argument" = "valor adicional de la
opción". Nunca te confundas con argc/argv: son cosas distintas.
Para guardar. Cuando veas
required_argument, leelo como "esta opción lleva un valor" --no como "requiere un argumento de función", que es otra cosa totalmente.
Práctica de la Parte 3
Ejercicio 13.4 -- Clasificar opciones por su macro
Conceptos · no_argument, required_argument, optional_argument
Contexto · Antes de escribir código, necesitás leer una lista de opciones y saber qué macro corresponde a cada una. Simplificación: solo elegís el macro correcto.
Consigna · Para cada opción, escribí qué macro corresponde en la tabla de struct option:
| Opción | ¿Lleva valor adicional? | Macro correcto |
|---|---|---|
--help |
no | ? |
--max 10 |
sí, el número | ? |
--output=fichero.txt |
sí, el nombre de archivo | ? |
--verbose (solo activa flag) |
no | ? |
--color (si acepta --color=auto ó solo --color) |
puede tener o no | ? |
Entrada · Las opciones listadas arriba. Salida · La tabla completada con los macros correctos.
Criterios de aceptación
no_argumentpara las que son puro interruptor (flag).required_argumentpara las que siempre llevan un valor.optional_argumentpara la que puede ir con o sin valor.
PARTE 4 -- El struct de opciones largas
4.1 ¿Qué es struct option?
Cada fila de tu tabla es una estructura de cuatro campos:
struct option {
const char *name; /* el nombre SIN los guiones */
int has_arg; /* no_argument, required_argument u optional_argument */
int *flag; /* NULL si querés comportamiento estándar */
int val; /* código corto que aparece en el switch */
};
Y lo usás así:
static struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"verbose", no_argument, 0, 'v'},
{"output", required_argument, 0, 'o'},
{"max", required_argument, 0, 'm'},
{0, 0, 0, 0} /* TERMINADOR */
};
La última fila es obligatoria: {0, 0, 0, 0}. Sin ella, getopt_long no sabe dónde termina tu
tabla y va a leer memoria de más --potencialmente un crash o comportamiento impredecible.
4.2 Desglose de cada campo
Campo 1: name -- el nombre largo sin los guiones
{"help", ...} /* corresponde a --help */
{"verbose", ...} /* corresponde a --verbose */
Acá escribís solo la palabra, sin los guiones. getopt_long sabe que si la tabla contiene
"help", eso significa --help. Nunca ponés los guiones en este campo.
Campo 2: has_arg -- ¿lleva valor?
Usás uno de los tres macros de la Parte 3:
{"help", no_argument, 0, 'h'}, /* --help --> flag, sin valor */
{"max", required_argument, 0, 'm'}, /* --max=10 o --max 10 --> lleva un valor */
Campo 3: flag -- lo que va a pasar con el retorno (casi siempre 0)
Este es el campo que más confusión genera. Tiene dos modos:
Modo A (el que vas a usar el 99% de las veces): flag = 0.
En este modo, getopt_long devuelve en cada vuelta el valor del cuarto campo (val). Así,
--help devuelve 'h', que coincide con -h, y ambos van al mismo case 'h':.
{"help", no_argument, 0, 'h'}, /* devuelve 'h' (como -h) */
Modo B: flag apunta a una variable. En este caso, getopt_long no usa el cuarto campo. En vez de
devolver un código, escribe 1 en la variable apuntada por flag y devuelve 0. Es útil para opciones
largas que no tienen equivalente corto, pero es un patrón avanzado que no vamos a usar acá.
Para este capítulo, siempre poné 0.
Campo 4: val -- el código que aparece en tu switch
Este es el valor de retorno cuando se encuentra la opción larga. Lo más común es que sea igual al código de la opción corta correspondiente:
{"help", no_argument, 0, 'h'}, /* --help devuelve 'h' */
{"verbose", no_argument, 0, 'v'}, /* --verbose devuelve 'v' */
Así, --help y -h caen en el mismo case 'h':. Escribís la lógica una vez y sirve para los dos.
Para guardar. El cuarto campo es el puente: une la opción larga con el código corto del
switch. Si pones'h', ambas variantes (-hy--help) van al mismocase.
4.3 El terminador: {0, 0, 0, 0}
Esta fila dice a getopt_long: "acá terminó mi tabla". Es un arreglo, no una lista con un conteo
adjunto --la única forma de saber dónde termina es encontrar ceros. Si la olvidás:
getopt_longlee memoria más allá del array → valores basura.- Probablemente termine en un crash o en comportamientos imposibles de diagnosticar.
- El error no se detecta en tiempo de compilación: aparece solo al ejecutar (si tenés suerte).
No la olvides. Nunca. La ponés siempre, sin excepciones, incluso si tu tabla tiene una sola opción.
4.4 Uniendo las partes: un ejemplo completo de tabla
static struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"verbose", no_argument, 0, 'v'},
{"debug", optional_argument, 0, 'd'},
{"output", required_argument, 0, 'o'},
{"max-retries", required_argument, 0, 'm'},
{0, 0, 0, 0}
};
Notá que "max-retries" tiene guión medio (-). Eso es normal: el nombre de la opción larga puede
llevar cualquier carácter que no sea -. Y sí, getopt_long entiende exactamente esa palabra como
--max-retries.
Ojo con esto. La tabla solo define opciones largas. Para las opciones cortas (
-h,-v), necesitás también eloptstring(el string del tercer parámetro degetopt_long). Si ponés una opción larga en la tabla pero no la incluís en eloptstring, igual funciona. Pero si ponés una corta en eloptstringy no en la tabla, solo funciona como corta (obvio).
Práctica de la Parte 4
Ejercicio 13.5 -- Escribir la tabla de opciones
Conceptos · struct option, los cuatro campos, terminador
Contexto · La tabla de opciones es el corazón de getopt_long: sin ella, no hay opciones largas.
Simplificación: escribir una tabla correcta para un conjunto dado de opciones.
Consigna · Escribí la declaración completa de long_options (array de struct option) para un
programa que acepta:
| Opción corta | Opción larga | ¿Lleva valor? |
|---|---|---|
-h |
--help |
no |
-v |
--verbose |
no |
-c |
--config-file |
sí (nombre de archivo) |
-t |
--timeout |
sí (número en segundos) |
No escribís la llamada a getopt_long ni el bucle. Solo la tabla.
Entrada · La tabla de opciones arriba. Salida · El código C completo para declarar el array.
Identificadores sugeridos · long_options
Criterios de aceptación
- Cada fila tiene los cuatro campos en orden.
nameno incluye los guiones ("help", no"--help").- El segundo campo usa el macro correcto para cada opción.
- La última fila es
{0, 0, 0, 0}. - Los valores del cuarto campo coinciden con las letras de las opciones cortas.
Ejercicio 13.6 -- Debuggear una tabla rota
Conceptos · errores comunes en struct option
Contexto · Las tablas mal escritas son la fuente más común de bugs con getopt_long. Simplificación:
encontrar los errores en una tabla dada.
Consigna · Esta tabla tiene tres errores. Encontralos y escribí qué está mal en cada fila:
static struct option long_options[] = {
{"help", required_argument, 0, 'h'},
{"verbose", no_argument, 0, "v"},
{"output", required_argument, NULL, 'o'},
{"max", no_argument, 0, 'm'}
};
Entrada · La tabla con errores arriba. Salida · Tres errores identificados, uno por fila (la última fila es correcta).
Criterios de aceptación
- Identificás el
has_argincorrecto enhelp. - Encontrás que
"v"es una cadena, no un char. - Reparás el
flag = NULLcuando debería ser0. - Reconocés que la fila de
maxestá correcta (es un distractor).
PARTE 5 -- Las variables globales: lo que getopt_long deja atrás
5.1 La analogía del cajero automático
Cuando vas a un cajero, le pasás tus datos y él guarda información internamente para el siguiente paso: tu número de cuenta, la fecha límite, etc. Vos no necesitás recordarlo --el cajero lo tiene en mente y te lo va preguntando.
getopt_long hace exactamente eso con tres variables globales: las modifica para que vos puedas
consultarlas cuando necesités un dato adicional. Estas variables están declaradas en <getopt.h>;
no las declarás vos, simplemente las usás.
5.2 optarg: el valor de la opción
Cada vez que getopt_long encuentra una opción que requiere un valor (-o archivo.txt,
--output archivo.txt o --output=archivo.txt), guarda ese valor en la variable global optarg.
while ((opt = getopt_long(argc, argv, "o:m:", ...)) != -1) {
switch (opt) {
case 'o':
printf("Archivo de salida: %s\n", optarg); /* <-- aca esta! */
break;
case 'm':
printf("Valor maximo: %s\n", optarg); /* <-- tambien aca */
break;
}
}
optarg es un char *. Puntero a cadena. Siempre contiene lo que viene después de la opción, ya
sea pegado con = o separado por espacio. No necesitás hacer aritmética de punteros: getopt_long
ya lo resolvió.
Momento ajá. Esto es exactamente lo que resolvíamos a mano en el capítulo anterior: sumar un offset al puntero para encontrar el valor después del
=. Acá, la función lo hace por vos y te lo deja en una variable lista para usar.
5.3 optind: donde empiezan los posicionales
getopt_long recorre argv, consumiendo todas las opciones que encuentra (cortas y largas). Cuando
termina, la variable global optind apunta al primer elemento de argv que NO es opción: el primer
argumento posicional.
while ((opt = getopt_long(argc, argv, "hv:", ...)) != -1) {
/* procesar opciones */
}
/* Aca optind == indice del primer argumento posicional */
printf("Posicionales:");
for (int i = optind; i < argc; i++) {
printf(" %s", argv[i]);
}
Podés consultar optind en cualquier momento. Tampoco lo modificás vos; getopt_long lo va
avanzando automáticamente a medida que consume opciones.
Truco de laboratorio. Podés verificar que
optindfunciona bien con este comando simple:./miapp -v --max=10 archivo.txt salida.txtCuando
getopt_longtermine,optinddebería valer3, porque los posicionales empiezan enargv[3]("archivo.txt").
5.4 optopt: la opcion desconocida
Cuando getopt_long encuentra una opción corta que no está en el optstring, guarda esa letra en
optopt. Es útil para mostrar un mensaje de error más preciso:
case '?':
fprintf(stderr, "Error: opcion desconocida '-%c'\n", optopt);
break;
Si la opción desconocida es larga (--desconocida), optopt vale 0. En ese caso, el mensaje de
error por defecto de getopt_long (que ya imprime algo como ./prog: unrecognized option '--desconocida')
ya es suficientemente claro.
5.5 getopt_reset(): reiniciar todo
Si necesitás pasar otra vez por los argumentos (por ejemplo, si llamaste a una función que también
usa getopt_long), podés llamar a:
getopt_reset();
Esto resetea optind, optarg, y el estado interno para que la próxima llamada a getopt_long vuelva
a recorrer desde el comienzo. En Windows con MSVC puro no está disponible (MinGW sí lo tiene). Si tu
codigo corre en MSVC, usá esta alternativa:
optind = 0;
Es un hack sucio pero que funciona. getopt_reset() es solo eso: resetear optind a cero para que la
próxima llamada recalcule donde empieza.
5.6 Por que globales? No es un bug, es un diseño
Las variables globas te pueden generar incomodidad: Que pasa si dos hilos llaman a getopt_long al
mismo tiempo? --nada, porque getopt_long no es thread-safe y punto. Las herramientas de consola
suelen ser monohebra. Usar globas para estas tres variables simplifica la API: no necesitás pasar un
puntero a contexto por cada llamada; simplemente lo consultás cuando querés.
Es un intercambio clasico de diseno: API mas simple ↔ no thread-safe. Para una herramienta de consola, es el cambio correcto.
Práctica de la Parte 5
Ejercicio 13.7 -- Completar el uso de variables globales
Conceptos · optarg, optind, optopt
Contexto · Dada una llamada a getopt_long con un esqueleto incompleto, completar las líneas que
usan las variables globales para mostrar los valores de las opciones. Simplificación: el programa es
minimo; solo usa optarg.
Consigna · Completá las líneas marcadas con /* ??? */ usando las variables globales correctas:
#include <getopt.h>
#include <stdio.h>
static struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"output", required_argument, 0, 'o'},
{"verbose", no_argument, 0, 'v'},
{0, 0, 0, 0}
};
int main(int argc, char *argv[])
{
int opt;
int option_index = 0;
while ((opt = getopt_long(argc, argv, "ho:", long_options, &option_index)) != -1) {
switch (opt) {
case 'h':
printf("Usando ayuda.\n");
break;
case 'o':
printf("Archivo: ???\n"); /* que variable tiene el valor? */
break;
case 'v':
printf("Verbose activo.\n");
break;
}
}
printf("Primer posicional: ???\n"); /* donde empiezan los posicionales? */
return 0;
}
Entrada · El código incompleto arriba. Salida · Las dos líneas completadas con la variable global correcta y una breve justificación.
Criterios de aceptación
- Completaste
'o'usandooptarg. - Completaste el posicional usando
argv[optind]. - Justificaste por qué cada variable es la correcta.
Ejercicio 13.8 -- Que vale optind despues de getopt_long?
Conceptos · optind, recorrido interno
Contexto · Entender qué pasa adentro cuando corré getopt_long. Simplificación: sin compilar,
adivinar el valor de optind después de que getopt_long termina.
Consigna · Para cada invocación, escribí cuánto vale optind cuando getopt_long termina:
./prog -v --max=10 archivo.txt salida.txt./prog --help./prog -hvo file.txt
Entrada · Las tres invocaciones arriba.
Salida · Tres valores de optind.
Criterios de aceptación
- Para la invocación 1, identificás que las opciones consumidas son
-v,--max=10. - Para la invocación 2, identificás que
--helpse consume y no quedan opcionales. - Para la invocación 3, entendés que
-hvoconsume tres letras cortas.
PARTE 6 -- Errores y el caso '?'
6.1 Qué pasa cuando algo sale mal
getopt_long no deja que los errores pasen desapercibidos. Si el usuario hace algo incorrecto, la
función devuelve '?' (el carácter punto de interrogación, valor ASCII 63) en lugar del código
de una opción válida.
Hay tres situaciones que generan un retorno '?':
| Situación | Ejemplo | Qué muestra getopt_long por defecto |
|---|---|---|
| Opción corta desconocida | -x (si -x no está en el optstring) |
./prog: invalid option -- 'x' |
| Opción larga desconocida | --desconocida |
./prog: unrecognized option '--desconocida' |
| Valor faltante | --max sin valor (cuando requiere uno) |
./prog: option '--max' requires an argument |
En todos los casos, el retorno es '?'. Tu trabajo es manejarlo con estilo.
6.2 El caso '?' en el switch
El patrón estándar es simple:
case '?':
/* Opción desconocida o valor faltante */
fprintf(stderr, "Uso: %s [opciones] <archivo>\n", argv[0]);
return 1;
Es un error de entrada: el usuario le pasó algo que tu programa no entiende. La respuesta correcta
es mostrar un mensaje de uso y terminar con código de error (return 1).
Ojo con esto.
getopt_longya imprimió su propio mensaje de error porstderrantes de devolver'?'. Si volvés a imprimir el mismo error, vas a tener dos líneas. Podés:
- Aceptar los dos mensajes (simplón).
- Anular el mensaje de
getopt_longimplementando una función de error personalizada (avanzado, no lo necesitamos acá).- Ignorar el segundo mensaje y simplemente mostrar la ayuda (recomendado para este capítulo).
6.3 El case default: por qué ponerlo siempre
Aunque ya cubriste todos los casos, poné siempre un default: en tu switch:
switch (opt) {
case 'h': /* ... */ break;
case 'v': /* ... */ break;
case 'o': /* ... */ break;
case '?': /* error */ break;
default: /* nunca se llega aqui, pero esta aca por seguridad */ break;
}
Es una red de seguridad. Si en el futuro agregás un case nuevo y te olvidás de uno, al menos el
programa no se compila (si tenés -Wdefault o similar). Con el default, la compilación pasa
tranquila, sabiendo que cubriste todo.
Práctica de la Parte 6
Ejercicio 13.9 -- Manejar errores con '?'
Conceptos · caso '?', stderr, códigos de salida
Contexto · Un programa que usa getopt_long necesita manejar el caso de opciones inválidas sin
crashear. Simplificación: detectar la opcion desconocida y mostrar una ayuda.
Consigna · Completá el esqueleto siguiente para que maneje correctamente el caso '?': cuando
getopt_long devuelve '?', el programa debe mostrar un mensaje de error por stderr con la línea
de uso, e inmediatamente retornar 1.
#include <getopt.h>
#include <stdio.h>
static struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"output", required_argument, 0, 'o'},
{0, 0, 0, 0}
};
int main(int argc, char *argv[])
{
int opt;
int option_index = 0;
while ((opt = getopt_long(argc, argv, "ho:", long_options, &option_index)) != -1) {
switch (opt) {
case 'h':
printf("Uso: %s [-h] [-o archivo]\n", argv[0]);
return 0;
case 'o':
printf("Archivo: %s\n", optarg);
break;
case '?':
/* COMPLETAR AQUI */
;
default:
break;
}
}
return 0;
}
Entrada · El código incompleto arriba.
Salida · El case '?' completado con el mensaje por stderr y el return 1.
Criterios de aceptación
- Escribís un
fprintf(stderr, ...)con la línea de uso. - Incluís
return 1;inmediatamente después delfprintf. - El código termina sin llegar al
default:en caso de error.
PARTE 7 -- Posicionales despues de optind
7.1 Los posicionales son lo que queda
Llegó el momento más sencillo: una vez que getopt_long terminó de procesar todas las opciones
(cortas y largas), los argumentos posicionales (archivos, nombres, datos "de verdad") quedan
acumulados al final de argv, empezando en la posición optind.
/* despues de que el while termina, optind apunta al primer posicional */
for (int i = optind; i < argc; i++) {
printf("Procesando: %s\n", argv[i]);
}
Es un bucle simple. No hay nada especial que hacer. getopt_long se encargó de separar las
opciones (que consumió una por una) de los posicionales (que dejó intactos al final).
7.2 La convencion de -- (fin de opciones)
Existe una convención en la línea de comandos: un argumento que es solo -- le dice a cualquier
programa POSIX: "de acá en adelante, todo es posicional, aunque empiece con guión".
./prog -- -archivo-que-empieza-con-guiones.txt
Acá, -archivo-que-empieza... no se interpreta como una opción porque -- ya marcó el límite.
getopt_long respeta esta convención automáticamente. Cuando ve un -- solo, deja de buscar opciones
y devuelve -1, dejando todo lo que sigue para optind.
7.3 Un ejemplo completo del flujo
#include <getopt.h>
#include <stdio.h>
static struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"verbose", no_argument, 0, 'v'},
{"output", required_argument, 0, 'o'},
{0, 0, 0, 0}
};
int main(int argc, char *argv[])
{
int opt;
int option_index = 0;
int verbose = 0;
const char *output_file = NULL;
/* Primero: procesar todas las opciones */
while ((opt = getopt_long(argc, argv, "ho:", long_options, &option_index)) != -1) {
switch (opt) {
case 'h':
printf("Uso: %s [-hv] [-o archivo] <archivos>...\n", argv[0]);
return 0;
case 'v':
verbose = 1;
break;
case 'o':
output_file = optarg;
break;
case '?':
fprintf(stderr, "Uso: %s [-hv] [-o archivo] <archivos>...\n", argv[0]);
return 1;
}
}
/* Segundo: procesar los posicionales (lo que quedaba) */
for (int i = optind; i < argc; i++) {
if (verbose) printf("Procesando: %s\n", argv[i]);
/* ... aqui iria la logica real de procesamiento ... */
}
return 0;
}
Acá tenés el flujo completo en acción: primero opciones, luego posicionales. El mismo patrón se repite
en todos los programas que usan getopt_long.
Práctica de la Parte 7
Ejercicio 13.10 -- Listar los posicionales despues de getopt_long
Conceptos · optind, bucle posicional, argc
Contexto · Dado un programa que procesó opciones con getopt_long, listar los archivos que quedaron.
Simplificación: solo imprimirlos, sin procesar.
Consigna · Completá la parte final del siguiente programa para que liste todos los argumentos
posicionales (desde optind hasta argc-1) después de que termina el bucle de opciones:
#include <getopt.h>
#include <stdio.h>
static struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"verbose", no_argument, 0, 'v'},
{"max", required_argument, 0, 'm'},
{0, 0, 0, 0}
};
int main(int argc, char *argv[])
{
int opt;
int option_index = 0;
int verbose = 0;
int max = 0;
while ((opt = getopt_long(argc, argv, "hvm:", long_options, &option_index)) != -1) {
switch (opt) {
case 'h':
printf("Uso: %s [-hv] [-m N] <archivos>...\n", argv[0]);
return 0;
case 'v':
verbose = 1;
break;
case 'm':
max = atoi(optarg);
break;
case '?':
fprintf(stderr, "Uso: %s [-hv] [-m N] <archivos>...\n", argv[0]);
return 1;
}
}
/* COMPLETAR: listar los posicionales despues de este comentario */
return 0;
}
El formato de salida para cada posicional debe ser: Archivo N: <nombre>, donde N es un contador
que empieza en 1.
Entrada · El código incompleto arriba. Salida · El bucle completado que lista los posicionales.
Criterios de aceptación
- El bucle recorre desde
optindhastaargc - 1. - Cada posicional se imprime con el formato correcto.
- El contador empieza en 1, no en 0.
Ejercicio 13.11 -- Unir opciones y posicionales
Conceptos · flujo completo getopt_long + optind, variables locales
Contexto · Escribir un programa minimo pero completo que use getopt_long con una opcion corta y
larga (con valor), procese los posicionales despues, e imprima un resumen. Simplificacion: no
procesar ningun archivo de verdad, solo reportar lo que recibio.
Consigna · Escribí el programa completo desde cero. Debe aceptar:
| Corta | Larga | Valor? | Accion |
|---|---|---|---|
-h |
--help |
no | Muestra la ayuda y termina con 0. |
-n |
--name |
si | Guarda el nombre del trabajo. |
Y despues de procesar opciones, lista los posicionales (si hay). El formato de salida debe ser:
Trabajo: <nombre>
Opciones leídas: -n/-verbose, --help=0
Archivos a procesar: <cantidad>
Archivo 1: <nombre>
Archivo 2: <nombre>
Criterios de aceptación
- Incluye
<getopt.h>y declara correctamentelong_options. - El optstring es
"hn:". - La tabla tiene dos filas con terminador.
-h/--helpmuestra ayuda y retorna 0.-n/--nameguarda el valor deoptargen una variable local.- El caso
'?'muestra error porstderry retorna 1. - Despues del bucle, recorre desde
optindhastaargclistando los posicionales. - El formato de salida coincide con el ejemplo.
PARTE 7.5 -- La ayuda de uso: imprimir una guia clara
7.5.1 No hay funcion estandar en C para esto
Acá va la primera noticia: C no tiene ninguna función estándar que genere automáticamente
una pantalla de ayuda formateada. Ni printf, ni getopt_long, ni nada en <stdio.h> o <getopt.h>
lo hace por vos. Cada programa imprime su propia ayuda manualmente con fprintf(stderr, ...) o
printf(...).
Eso no es un defecto: es flexibilidad. Algunas herramientas muestran una ayuda corta de una línea; otras, una pantalla completa con descripciones detalladas. Vos decis cómo se ve.
Lo que sí existe es un formato estándar de facto que usan casi todas las herramientas POSIX —ls,
grep, tar, curl— y que el usuario ya reconoce sin pensar. Aprender ese formato es tan importante
como aprender a usar getopt_long.
7.5.2 El formato estandar: tu ejemplo como plantilla
Este es el formato canónico que vas a seguir. Es el mismo que usás en tu ejemplo, con ligeras mejoras:
Uso: mi_programa [opciones] <archivo_origen> <archivo_destino>
Argumentos posicionales:
archivo_origen Ruta del archivo que se va a leer (obligatorio, posición 1).
archivo_destino Ruta del archivo donde se guardará el resultado (obligatorio, posición 2).
Opciones:
-h, --help Muestra esta ayuda y termina.
-v, --verbose Activa el modo detallado.
Desglosémoslo por secciones.
La línea de Uso
Uso: mi_programa [opciones] <archivo_origen> <archivo_destino>
Esta primera línea dice todo en un vistazo. Sigue reglas fijas de notación:
| Notacion | Significado | Ejemplo |
|---|---|---|
[opcionales] |
Lo que va entre corchetes es opcional —se puede omitir. | [-hv] [-o archivo] |
<obligatorio> |
Los angulares indican un argumento obligatorio. | <archivo_origen> |
palabra |
Sin nada: lo que se escribe tal cual, como --help. |
--verbose |
La estructura típica es siempre:
Uso: <nombre_programa> [opciones] <posicionales_obligatorios>
NUNCA uses corchetes para envolver todo el uso —eso confunde. La línea debe ser tan clara que alguien la lea una vez y sepa cómo ejecutar el programa.
Argumentos posicionales
La sección de posicionales va primero (antes de las opciones) porque son la parte principal del programa: sin ellos, nada tiene sentido. Cada posicional lleva:
- El nombre del argumento (en
snake_case, minúsculas). - Un tabulador o dos espacios.
- La descripción.
- Entre paréntesis al final: si es obligatorio u opcional, y su posición numérica.
Argumentos posicionales:
archivo_origen Ruta del archivo que se va a leer (obligatorio, posición 1).
archivo_destino Ruta del archivo donde se guardará el resultado (obligatorio, posición 2).
modo Modo de procesamiento: "normal" o "agresivo" (opcional, posición 3, valor por defecto: "normal").
Opciones
La sección de opciones va después. Cada opción muestra la forma corta primero, luego la larga, separadas por coma. La descripción comienza en la misma columna que todas las demás descripciones:
Opciones:
-h, --help Muestra esta ayuda y termina.
-v, --verbose Activa el modo detallado.
-o, --output <archivo> Guarda el resultado en el archivo indicado.
-m, --max <N> Número máximo de elementos a procesar (por defecto: 100).
Notá que las opciones con valor incluyen <nombre_del_valor> entre la opción y la descripción.
Las opciones sin valor van directo a la descripción.
7.5.3 Un helper para no repetir codigo
Aunque no hay función estándar, el patrón mas usado es crear una funcion propia llamada print_help
que recibirá el nombre del programa (generalmente argv[0]) y escribirá toda la ayuda de golpe.
Esto evita repetir el texto en cinco lugares distintos (caso '?', caso -h, caso falta de argumentos, etc.)
y garantiza que siempre se muestra el mismo texto.
#include <stdio.h>
/* Declaramos la funcion antes de main: recibe el nombre del programa y muestra la ayuda por stdout. */
static void print_help(const char *progname)
{
printf("Uso: %s [opciones] <archivo_origen> <archivo_destino>\n", progname);
printf("Argumentos posicionales:\n");
printf(" archivo_origen Ruta del archivo que se va a leer (obligatorio, posicion 1).\n");
printf(" archivo_destino Ruta del archivo donde se guardara el resultado (obligatorio, posicion 2).\n");
printf("\n");
printf("Opciones:\n");
printf(" -h, --help Muestra esta ayuda y termina.\n");
printf(" -v, --verbose Activa el modo detallado.\n");
printf(" -o, --output <archivo> Guarda el resultado en el archivo indicado.\n");
printf(" -m, --max <N> Numero maximo de elementos a procesar (por defecto: 100).\n");
}
Despues, la llamás desde cualquier lugar donde necesites mostrar ayuda:
case 'h':
print_help(argv[0]);
return 0;
case '?':
fprintf(stderr, "Error: opcion invalida.\n\n");
print_help(argv[0]);
return 1;
Momento ajá. La ventaja de
print_helpno es solo que escribís menos código: es que si mañana agregás una opción nueva, la modificás en un solo lugar (print_help) y todas las salidas se actualizan automáticamente. Sin ella, tendrías que buscar y reemplazar cada aparición del texto de ayuda —y muy probablemente te olvidás de una.
7.5.4 stdout vs stderr para la ayuda
La ayuda tiene un comportamiento mixto:
- Cuando el usuario pide la ayuda explícitamente (
-ho--help): va porstdout. El usuario pidió ver algo; que salga en pantalla sin interferir con redirecciones es lo correcto. - Cuando la ayuda aparece como consecuencia de un error (opción desconocida, falta de argumentos):
va por
stderr, porque está acompañando un mensaje de error.
Esto permite dos flujos distintos:
./mi_programa --help > ayuda.txt # el usuario guardó la ayuda en un archivo
./mi_programa --desconocida # el error con ayuda aparece en pantalla, no se pierde
Práctica de la Parte 7.5
Ejercicio 13.12 -- Crear la funcion print_help
Conceptos · printf, estructura de ayuda, separación stdout/stderr
Contexto · Dado un conjunto de opciones y posicionales, escribir la función print_help que los
muestre con el formato estándar del capítulo. Simplificacion: no escribís el programa principal, solo
la funcion de ayuda.
Consigna · Escribí una función print_help(const char *progname) para un programa que tiene:
| Corta | Larga | Valor? | Descripcion |
|---|---|---|---|
-h |
--help |
no | Muestra esta ayuda y termina. |
-q |
--quiet |
no | Suprime los mensajes informativos. |
-d |
--database |
si | Especifica la base de datos a conectar. |
Y que espera dos argumentos posicionales:
archivo_conf— Ruta del archivo de configuración (obligatorio, posición 1).archivo_log— Ruta del archivo de registro de eventos (opcional, posición 2, valor por defecto:/dev/null).
El formato debe seguir exactamente la plantilla de la sección 7.5.2, con las secciones en este orden: Uso → Argumentos posicionales → Opciones.
Entrada · Los datos arriba.
Salida · La función C completa print_help.
Identificadores sugeridos · progname
Criterios de aceptación
- La línea de Uso usa
prognamey la notación correcta (<>,[]). - Los posicionales se listan con sus descripciones completas, incluyendo obligatorios/opcionales y posiciones.
- Las opciones muestran ambas variantes (corta/larga) separadas por coma.
- Las opciones con valor incluyen
<nombre>entre la opción y la descripción. - Hay una línea en blanco entre la sección de posicionales y la de opciones.
Ejercicio 13.13 -- Usar print_help en el programa completo
Conceptos · getopt_long, print_help, stdout/stderr, flujo de ayuda
Contexto · Integrar print_help en un programa real con getopt_long. Simplificacion: el
programa solo muestra la ayuda y termina; no procesa ningun dato.
Consigna · Escribí un programa completo que:
- Use
getopt_longcon las opciones-h,-q,-d <database>. - Llame a
print_help(argv[0])(definida por vos siguiendo el formato estándar) cuando se detecta-h/--help, retornando 0. - Muestre
print_help(argv[0])porstderry retorne 1 cuandogetopt_longdevuelve'?'. - Si no recibe el argumento posicional obligatorio (
archivo_conf), muestre la ayuda porstderrcon un mensaje de error antes y retorne 1. - Si todo está bien, imprima:
Conectando a base de datos: <valor_de_d> desde <archivo_conf>porstdouty retorne 0.
Escenario de prueba
$ ./conector --help
Uso: ./conector [opciones] <archivo_conf> [archivo_log]
Argumentos posicionales:
archivo_conf Ruta del archivo de configuración (obligatorio, posición 1).
archivo_log Ruta del archivo de registro (opcional, posición 2, por defecto: /dev/null).
Opciones:
-h, --help Muestra esta ayuda y termina.
-q, --quiet Suprime los mensajes informativos.
-d, --database <database> Especifica la base de datos a conectar.
$ ./conector
Error: se requiere un archivo de configuración
Uso: ./conector [opciones] <archivo_conf> [archivo_log]
... (resto de la ayuda por stderr)
$ ./conector -d mi_db config.ini
Conectando a base de datos: mi_db desde config.ini
Criterios de aceptación
print_helpexiste como funcion estatica y se llama desde-h,'?', y falta de argumentos.- La línea de Uso coincide exactamente con el formato canónico.
- El caso
'?'imprime un error porstderrANTES de mostrar la ayuda (tambien porstderr). -hmuestra todo porstdouty retorna 0.- Falta de posicional obligatorio muestra error + ayuda por
stderr, retorna 1. - Flujo exitoso imprime el mensaje correcto por
stdout, retorna 0.
PARTE 8 -- El ejercicio jefe
8.1 La prueba final
Llegó el momento de juntar todo lo aprendido en un solo programa. Este no es un ejercicio cualquiera:
es una herramienta de consola completa que usa getopt_long para interpretar opciones cortas,
opciones largas con valores, y deja los posicionales intactos al final.
Ejercicio 13.14 -- Utilidad de consola completa (JEFE)
Conceptos · getopt_long, struct option, optarg, optind, stderr, códigos de salida,
variables locales, bucle posicional, print_help.
Contexto · Reunir todo el capítulo en una sola herramienta real de línea de comandos. Es una simplificación: no procesa archivos de verdad, pero su interfaz es la de una utilidad profesional.
Consigna · Desarrollá un programa que interprete estas opciones usando getopt_long:
| Corta | Larga | ¿Valor? | Acción |
|---|---|---|---|
-h |
--help |
no | Muestra la ayuda y termina con 0. |
-v |
--verbose |
no | Activa el modo detallado. |
-o |
--output |
sí | Guarda el archivo de salida indicado. |
-m |
--max |
sí | Guarda el máximo (número entero). |
Y que además:
- Defina una funcion
static void print_help(const char *progname)siguiendo el formato estándar de la Parte 7.5. - Use
print_help(argv[0])tanto para-h/--help(porstdout, retorno 0) como para errores por'?'(porstderr, retorno 1). - Acepte las dos formas de valor para las largas (
--output=xy--output x). Esto lo resuelvegetopt_longautomáticamente --no necesitás hacer nada extra. - Trate los argumentos no-opción como archivos posicionales (listados al final usando
optind). - Si no recibe ningún posicional ni
--output, avise que no hay nada que hacer (error claro).
Entrada · Argumentos por línea de comandos.
Salida · El efecto de las opciones por stdout; los errores por stderr.
Escenario de prueba
$ ./util --verbose --max=10 entrada.txt salida.txt
Modo verbose: ON
Maximo: 10
Archivos a procesar: 2
Archivo 1: entrada.txt
Archivo 2: salida.txt
$ ./util -v --max=5 datos.csv
Modo verbose: ON
Maximo: 5
Archivos a procesar: 0
Aviso: no hay archivos de entrada ni archivo de salida definido
$ ./util --help
Uso: ./util [opciones] <archivos>...
Argumentos posicionales:
archivos Lista de archivos a procesar (al menos uno obligatorio).
Opciones:
-h, --help Muestra esta ayuda y termina.
-v, --verbose Activa el modo detallado.
-o, --output <archivo> Archivo de salida.
-m, --max <N> Maximo de elementos (por defecto: 100).
Identificadores sugeridos · opt, long_options, option_index, optind, verbose,
max_value, output_name, i
Restricciones / pistas
- Usá
getopt_longcon eloptstring"hvo:m:". - No olvides el terminador
{0,0,0,0}de la tabla. - El
-htiene prioridad: mostralo apenas aparece y terminá. - Los posicionales salen de
argv[optind..argc-1]. - Errores →
stderr; resultados →stdout. - Si no hay archivos ni output definido, mostrá un aviso (no es error fatal, pero sí informativo).
Criterios de aceptación
- Reconoce
-v/--verbose,-o/--outputy-m/--max. - Acepta valor pegado (
--max=10) y separado (--max 10). print_helpexiste como funcion estatica con el formato canónico.-h/--helpmuestra ayuda porstdouty retorna 0.- Una opción desconocida produce error por
stderr(con help incluido) y código != 0. - Los posicionales se listan después de las opciones (usando
optind). - El formato coincide con los escenarios de prueba.
- Sin archivos ni output, muestra un aviso informativo.
Cierre: lo que te llevás
Si tuvieras que quedarte con una sola idea, que sea esta: getopt_long hace todo el trabajo pesado
de parsear opciones para vos --vos solo declarás qué opciones reconocés y qué querés hacer con cada una.
- La llamada es siempre la misma.
getopt_long(argc, argv, "hvo:m:", long_options, &option_index). Cambiá el optstring, cambiá la tabla, pero el patrón no cambia. struct optiontiene cuatro campos. Nombre sin guiones, macro de argumento,0, código corto. Y el terminador{0,0,0,0}al final --siempre.- Tres macros definen si lleva valor.
no_argument(flag),required_argument(lleva siempre un valor),optional_argument(puede llevar o no). - Tres variables globales hacen el trabajo sucio.
optargtiene el valor de la opción,optindapunta al primer posicional,optopttiene la letra de la opción desconocida. optindes tu puente hacia los datos reales. Después del buclewhile, unfordesdeoptindhastaargcte da todos los posicionales --archivos, nombres, lo que importa.- El caso
'?'captura todos los errores. Si la opción es desconocida o falta un valor,getopt_longdevuelve'?'. Manejalos mostrando ayuda porstderry retornando 1. - La ayuda se imprime con un helper propio. No existe funcion estandar en C para generarla automaticamente;
el patrón es crear una
print_help(const char *progname)que centralice el texto y lo muestre siguiendo el formato canónico (Uso → posicionales → opciones).
Y sobre todo: aprendiste a delegar. Escribir un parser de opciones desde cero es posible (y lo hiciste
en el capítulo anterior), pero getopt_long lo hace mejor, más consistente, y con menos líneas de código.
Eso no es trampa --es programar inteligente.