Files
c-programming/book/13-argumentos-de-linea-de-comandos.md
T

1445 lines
50 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Capítulo 13 — Argumentos de línea de comandos: de cero a pro
> Capítulo intermedio, pero **escrito para que se entienda de verdad**. Se apoya en variables (`02`),
> operadores (`03`), `if` y bucles (`04`), funciones (`05`), arreglos y cadenas (`06`) y punteros (`07`).
>
> Es la **antesala** del capítulo de entrada/salida estándar (`14-entrada-salida-estandar`). Acá
> aprendés a que tu programa reciba **órdenes al nacer** (cuando lo ejecutás) en lugar de pedirlas
> recién mientras corre. En el capítulo siguiente vas a profundizar en las tres tuberías
> (`stdin`/`stdout`/`stderr`), que acá tocamos apenas lo justo.
---
## Cómo leer este capítulo
Hasta ahora, todos tus programas empezaban **preguntando**. Le mostraban un cartel al usuario y
esperaban a que escribiera algo por teclado. Eso funciona, pero no es la única forma. Las herramientas
serias —`ls`, `grep`, `git`, `gcc`— no te preguntan nada: **les pasás las órdenes en el mismo momento
en que las ejecutás**.
Ese es el tema de este capítulo.
Está dividido en **nueve partes**. Después de cada idea importante hay una **práctica** —uno o varios
ejercicios cortos— para que la uses ya mismo, antes de avanzar. **No saltees las prácticas**: son la
mitad del capítulo.
| Parte | De qué habla | La idea en una frase |
|---|---|---|
| **1** | Órdenes al nacer | Tu programa puede recibir los datos cuando lo ejecutás, no después. |
| **2** | `argc` y `argv` | El par mágico que `main` recibe con la cantidad y la lista de argumentos. |
| **3** | `argv[0]` | El primer argumento siempre es **cómo llamaron** a tu programa. |
| **4** | Recorrer y clasificar | Cómo leer los argumentos uno por uno y distinguir qué tipo es cada uno. |
| **5** | Opciones cortas y largas | `-v` vs `--verbose`: dos estilos que conviven en toda herramienta. |
| **6** | Opciones con valor | `--dato=2` o `--dato 2`: cómo leer el valor que acompaña a una opción. |
| **7** | Validar y avisar | Errores por `stderr`, códigos de salida y un `--help` digno. |
| **8** | `getopt` | El parseo ordenado de opciones cortas, con soporte para `-abc` apiladas. |
| **9** | `getopt_long` | El nivel pro: opciones largas al estilo `--output=archivo`. |
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 — Tu programa puede recibir órdenes al nacer
Antes de tocar una sola línea de código, entendamos **qué cambia** y **por qué** esto es tan útil.
## 1.1 La diferencia entre preguntar y que te digan
Pensá en una **máquina expendedora**.
- **Forma antigua (preguntar durante la ejecución):** prendés la máquina y ella te va preguntando:
*"¿Qué querés?"*. Vos respondés por un teclado mientras la máquina ya está funcionando. En C, eso es
usar `scanf`, `getchar` o `fgets` sobre `stdin`: el programa **se frena y espera**.
- **Forma nueva (recibir órdenes al nacer):** **le das los ingredientes ANTES de encenderla**. Le decís:
*"2 latas de Coca-Cola"*. La ejecutás y ya sabe qué hacer: **no pregunta, va al resultado**.
Eso es exactamente lo que pasa cuando escribís en la terminal:
```bash
./programa test.txt --alguna-opcion --dato=2
```
Le estás pasando **órdenes al programa en el mismo momento en que lo ejecutás**. Son los **argumentos
de línea de comandos** (en inglés, *command-line arguments*).
> **Momento ajá.** La diferencia clave es el **momento**: los argumentos llegan **al arrancar el
> programa** (cuando empieza `main`), no durante su ejecución.
## 1.2 ¿Por qué esto importa?
Porque es **el** mecanismo con el que se automatiza trabajo en la vida real.
- Un script de respaldo hace `./backup --origen=/data --destino=/backup --comprimir`.
- Un analizador de logs hace `./analiza server.log --desde=2026-01-01 --nivel=error`.
- El compilador hace `gcc -o salida programa.c -Wall`.
Nadie está sentado respondiendo preguntas: los datos **viajan con la orden**. Eso permite que un
programa llame a otro, encadenarlos con tuberías y automatizarlos con tareas programadas.
## 1.3 Lo que ya conocés, y lo que viene
Ya sabés que `main` es la puerta de entrada del programa. Su forma mínima es:
```c
int main(void)
{
/* ... */
return 0;
}
```
Ese `int` de adelante es el **código de retorno**: `0` significa "salió bien". Ya lo veníamos usando.
Para recibir argumentos, `main` admite **dos parámetros extra**. Esa es toda la novedad:
```c
int main(int argc, char *argv[])
{
/* ... */
return 0;
}
```
No pasa nada raro: es la misma función `main`, con dos datos de entrada más. En la Parte 2 los
desarmamos pieza por pieza.
## Práctica de la Parte 1
### Ejercicio 13.1 — Pensar antes de ejecutar
**Conceptos** · argumentos de línea de comandos, diferencia con la lectura por teclado
**Contexto** · Antes de escribir código, hay que entender **cuándo** llegan los datos. Simplificación:
dos formas de pedirle algo a un programa.
**Consigna** · Leé estas dos escenas y respondé por escrito (sin programar):
1. Ejecutás `./programa`, aparece el cartel `¿Archivo?` y vos escribís `datos.txt`.
2. Ejecutás `./programa datos.txt` directamente desde la terminal.
¿En cuál de las dos el programa **ya sabe qué archivo usar desde el arranque**? ¿Cuál corresponde a
"argumentos de línea de comandos"?
**Entrada** · Ninguna (es un ejercicio de análisis).
**Salida** · Una respuesta escrita comparando ambas formas.
**Escenario de prueba** · No aplica (no hay programa).
**Criterios de aceptación**
- [ ] Identificás que en la escena 2 los datos llegan **al ejecutar**.
- [ ] Explicás la diferencia entre pedir datos **durante** la ejecución y **al ejecutarla**.
- [ ] Nombrás correctamente la forma 2 como "argumentos de línea de comandos".
---
# PARTE 2 — `argc` y `argv`: el par mágico
## 2.1 `argc`: el contador
Cuando ejecutás un programa desde la consola, el **sistema operativo** (a través del shell) le entrega
a `main` **cuántas** palabras escribiste y **cuáles** fueron.
El primero de los dos parámetros es `argc`:
- Significa **"argument count"**: *cantidad de argumentos*.
- Es un `int`.
- Cuenta **cuántas palabras** separadas por espacios llegaron en la línea de comandos.
**Analogía.** Imaginá una lista de compras escrita en un papel. `argc` es **el número de ítems** del
papel. No te dice qué comprar: solo **cuántas cosas** hay.
> **Para guardar.** `argc` **nunca es 0** en un programa ejecutado normalmente. Siempre cuenta al
> menos **1**. ¿Por qué? Lo vas a ver en la Parte 3 (spoiler: el nombre del programa también cuenta).
## 2.2 `argv`: la lista
El segundo parámetro es `argv`:
- Significa **"argument vector"**: *vector (lista) de argumentos*.
- Es un **arreglo de cadenas**: `char *argv[]`.
- Cada posición guarda **una palabra** exactamente como apareció en la terminal.
**Analogía.** Si `argc` es el número de ítems de la lista, `argv` es **la lista misma**: un estante
donde cada balda guarda una palabra completa. `argv[0]` es el primer estante, `argv[1]` el segundo, y
así hasta `argv[argc-1]`.
> **Un detalle jurídico.** `argv` es un arreglo de **punteros a `char`** (`char *`). Cada elemento
> apunta al comienzo de una cadena terminada en `'\0'`. Por eso se escribe `char *argv[]` y no
> `char argv[]`: no guardamos las letras, guardamos **la dirección** de dónde empiezan. Si todavía
> tenés dudas con punteros, repasá el capítulo `07-punteros`.
## 2.3 El ejemplo completo, palabra por palabra
Si ejecutás:
```bash
./programa test.txt --alguna-opcion --dato=2
```
el sistema reparte los argumentos así:
| Índice | Valor de `argv[i]` | ¿Qué es? |
|---|---|---|
| `0` | `"./programa"` | El nombre con el que se ejecutó. |
| `1` | `"test.txt"` | Un argumento pasado. |
| `2` | `"--alguna-opcion"` | Otro argumento. |
| `3` | `"--dato=2"` | Otro más. |
Y `argc` vale **4** (hay 4 palabras). Fijate que `argc` **cuenta también el nombre**: por eso
`argv[0]` a `argv[3]` son 4 posiciones, y `argc = 4`.
> **Ojo con esto.** El **espacio** es lo que separa un argumento del siguiente. Cada bloque entre
> espacios se convierte en un `argv[i]` distinto. Si querés que un texto con espacios sea **un solo**
> argumento, hay que **encerrarlo entre comillas**: `./programa "hola mundo"` llega como un único
> `argv[1]` igual a `"hola mundo"`.
## 2.4 Diagrama mental
```text
Línea: ./programa test.txt --alguna-opcion --dato=2
│ │ │ │
▼ ▼ ▼ ▼
argv ─► ┌────────┐┌─────────┐┌──────────────┐┌─────────┐
│[0] ││[1] ││[2] ││[3] │ ...
│"./prog"││"test.txt"││"--alguna-op" ││"--dato=2"│
└────────┘└─────────┘└──────────────┘└─────────┘
argc = 4 (cuatro palabras, incluido el nombre)
```
## 2.5 El caso más chico posible
Si ejecutás:
```bash
./saludo
```
obtenés:
- `argc = 1`
- `argv[0] = "./saludo"`
Aunque **no pasaste nada extra**, `argc` vale 1: el nombre del programa **siempre** ocupa el estante 0.
## Práctica de la Parte 2
### Ejercicio 13.2 — Contar y adivinar
**Conceptos** · `argc`, `argv`, índice de argumentos
**Contexto** · Antes de programar, hay que **leer** una línea de comandos como la leería el sistema.
Simplificación: adivinar valores sin ejecutar nada.
**Consigna** · Dada la invocación:
```bash
./programa archivo.txt -v 42
```
respondé sin programar:
1. ¿Cuánto vale `argc`?
2. ¿Qué contiene `argv[0]`, `argv[1]`, `argv[2]` y `argv[3]`?
3. ¿Por qué `argc` **no** vale 3?
**Entrada** · La línea de comandos del enunciado.
**Salida** · Las tres respuestas.
**Escenario de prueba** · No aplica (no hay programa).
**Criterios de aceptación**
- [ ] `argc` correcto (recordá que el nombre cuenta).
- [ ] Los cuatro valores de `argv` correctos y en orden.
- [ ] Justificás el punto 3 mencionando que `argv[0]` es el nombre del programa.
### Ejercicio 13.3 — Eco de argumentos
**Conceptos** · `argc`, `argv`, bucle `for`, `%s`
**Contexto** · El primer programa "de verdad" con argumentos: repetir lo que recibió. Simplificación:
un eco que no interpreta nada, solo muestra.
**Consigna** · Escribí un programa que, al ejecutarlo, muestre por pantalla **la cantidad** de
argumentos y luego **cada argumento** en una línea, en el formato del escenario de prueba. Recorré el
arreglo con un bucle `for`.
**Entrada** · Los argumentos que le pases al ejecutarlo.
**Salida** · Una línea con la cantidad y una línea por argumento, con el formato exacto del ejemplo.
**Escenario de prueba**
```text
$ ./eco hola mundo 123
argc = 4
argv[0] = ./eco
argv[1] = hola
argv[2] = mundo
argv[3] = 123
```
**Identificadores sugeridos** · `argc`, `argv`, `i`
**Restricciones / pistas**
- El bucle debe llegar **hasta `argc-1`**: nunca leas `argv[argc]` (estarías fuera del arreglo).
- Empezás en `i = 0` si querés mostrar **todos** (incluido el nombre). En el próximo ejercicio
empezarás en `i = 1`.
**Criterios de aceptación**
- [ ] Muestra el valor correcto de `argc`.
- [ ] Recorre de `0` a `argc-1` con un `for`.
- [ ] El formato coincide exactamente con el escenario de prueba.
- [ ] No accede a ninguna posición fuera del rango válido.
---
# PARTE 3 — `argv[0]`: el nombre del programa
## 3.1 El argumento número cero es especial
`argv[0]` guarda **cómo se llamó al programa** en esa ejecución concreta. No es un dato que pasaste
vos: es el nombre con el que el sistema lo invocó.
**Analogía.** Es como presentarte: *"Hola, soy Horacio"*. `argv[0]` es **el nombre con el que te
llamaron** en ese momento, no quién sos en realidad.
Puede ser una ruta relativa (`./programa`), absoluta (`/usr/bin/programa`) o, en Windows, incluir
`.exe` (`programa.exe`). Todo depende de **cómo lo escribiste al ejecutarlo**.
## 3.2 ¿Para qué sirve saber el nombre?
Para **mensajes honestos**.
Cuando un programa necesita decirle al usuario cómo usarlo (un mensaje de "Uso: ..."), lo correcto es
mostrar **el mismo nombre con el que lo llamaron**, no uno fijo escrito a mano.
- Si el usuario lo ejecutó como `./mini`, tu ayuda debería decir `Uso: ./mini ...`.
- Si lo ejecutó como `mini`, debería decir `Uso: mini ...`.
**Nunca hardcodees el nombre del ejecutable.** Usá `argv[0]`.
> **Momento ajá.** El truco `printf("Uso: %s <archivo>\n", argv[0]);` es uno de los patrones más
> repetidos de la programación de consola. Guardátelo.
## 3.3 Un caso de borde
`argv[0]` **puede estar vacío** (`""`) en situaciones muy raras de algunos sistemas, pero en la
práctica casi nunca ocurre. Lo normal es que tenga el nombre, relativo o absoluto.
## Práctica de la Parte 3
### Ejercicio 13.4 — El nombre con el que me llamaron
**Conceptos** · `argv[0]`, cadenas, `printf` con `%s`
**Contexto** · Todo programa de consola debería mostrar su propio nombre en las ayudas. Simplificación:
mostrar el nombre y una línea de uso genérica.
**Consigna** · Escribí un programa que muestre su nombre (`argv[0]`) y un mensaje de uso que lo
incluya **dinámicamente**, con el formato del escenario de prueba.
**Entrada** · Cualquier cosa (el programa no necesita argumentos extra).
**Salida** · Dos líneas con el formato exacto del ejemplo.
**Escenario de prueba**
```text
$ ./demo archivo.txt
Programa: ./demo
Uso: ./demo <archivo> [opciones]
```
**Identificadores sugeridos** · `argv`
**Restricciones / pistas**
- El nombre del programa **no** debe escribirse literal: usá `argv[0]`.
- Si lo ejecutás con otra ruta (`./carpeta/demo`), el mensaje debe reflejarlo.
**Criterios de aceptación**
- [ ] Muestra `argv[0]` tal cual.
- [ ] El mensaje de uso usa `argv[0]`, no un texto fijo.
- [ ] Cambia correctamente si el programa se ejecuta con otra ruta.
---
# PARTE 4 — Recorrer y clasificar argumentos
## 4.1 El patrón para recorrer (sin salirse nunca)
Para leer los argumentos que **le pasaste al programa** (no el nombre), se recorre desde `argv[1]`
**hasta `argv[argc-1]`**. La condición del bucle es **siempre** `i < argc`.
```c
#include <stdio.h>
int main(int argc, char *argv[])
{
/* Recorro desde 1: el 0 (argv[0]) es el nombre del programa. */
for (int i = 1; i < argc; i++) {
printf("Argumento %d: %s\n", i, argv[i]);
}
return 0;
}
```
- **Empezamos en 1** porque `argv[0]` es el nombre, no un dato de tu tarea.
- **Terminamos cuando `i == argc`** porque la última posición válida es `argc-1`.
> **Trampa N.º 1 — salirse del arreglo.** Si la condición fuera `i <= argc`, en algún momento leerías
> `argv[argc]`, que **no existe**: es memoria basura. El resultado es un *segfault* o datos
> impredecibles. La condición correcta es **`i < argc`, sin excepción**.
## 4.2 Ahora sí: no todos los argumentos son iguales
En `./programa test.txt --alguna-opcion --dato=2` hay **tres clases** de argumentos. Distinguirlos es
el corazón del parseo.
| Clase | Ejemplo | Pista para reconocerlo |
|---|---|---|
| **Posicional** | `test.txt` | **No** empieza con `-`. Su **posición** importa (es el "primer dato", el "segundo dato"...). |
| **Opción (flag)** | `--alguna-opcion` | Empieza con `-` o `--` y **no** lleva valor. Activa/desactiva algo. |
| **Opción con valor** | `--dato=2` | Es una opción **y** trae su valor (con `=` o en el argumento siguiente). |
**Analogía de pedido por delivery.**
- `pedido.txt` → **qué** hay que procesar (posicional).
- `--rapido` → **una opción**: "querés rápido" (sí/no).
- `--cantidad=2` → **opción con valor**: "querés 2".
## 4.3 La regla del guión
La pista principal para reconocer una opción es simple: **empieza con `-`**.
```text
"-" ... no es opción (guión solo: suele significar "leer de stdin")
"-x" ... opción corta
"--" ... no es una opción (separador especial "fin de opciones")
"--x" ... opción larga
"x" ... argumento posicional
```
Cuidado con los casos de la tabla: un `"-"` solito **no** es una opción, y un `"--"` solito tampoco
(suele marcar *"de acá en adelante, todo es posicional"*). A esos casos raros les prestamos atención
en la Parte 5.
> **Trampa N.º 2 — confundir posición con opción.** Los **posicionales** dependen del **orden**: el
> primer nombre de archivo es el de entrada, el segundo el de salida. Las **opciones** suelen poder ir
> en cualquier orden. No mezcles ambas ideas.
## 4.4 Un esqueleto de clasificación (patrón, no solución)
Para clasificar a mano, un patrón típico es mirar la forma del argumento:
```c
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-' && argv[i][1] == '-')
/* opción larga */;
else if (argv[i][0] == '-')
/* opción corta */;
else
/* posicional */;
}
```
Fijate los índices: `argv[i][0]` es el **primer carácter** de la palabra; `argv[i][1]` el **segundo**.
Por eso `argv[i][0] == '-'` pregunta "¿empieza con guión?" y `argv[i][1] == '-'` pregunta "¿y el
siguiente también?" (o sea, `--`).
> **Ojo con esto.** Este es un **patrón ilustrativo** para entender cómo se mira una cadena por dentro.
> Cuidado con los **casos de borde** (`"-"` y `"--"`): el patrón de arriba los trataría como opciones.
> Un clasificador robusto los contempla aparte.
## Práctica de la Parte 4
### Ejercicio 13.5 — Listar solo los parámetros útiles
**Conceptos** · `argc`, `argv`, bucle, índices
**Contexto** · Muchas herramientas listan lo que recibieron, sin el nombre del programa.
Simplificación: un listado de los argumentos pasados.
**Consigna** · Escribí un programa que muestre **únicamente** los argumentos posteriores al nombre
(desde `argv[1]`), uno por línea, con el formato del escenario de prueba.
**Entrada** · Los argumentos pasados al ejecutarlo.
**Salida** · El listado pedido, sin incluir `argv[0]`.
**Escenario de prueba**
```text
$ ./filtrar -r --debug datos.csv
Parámetros recibidos:
- -r
- --debug
- datos.csv
```
**Identificadores sugeridos** · `argc`, `argv`, `i`
**Restricciones / pistas**
- Recorré desde `i = 1`.
- No imprimas `argv[0]`.
**Criterios de aceptación**
- [ ] No muestra el nombre del programa.
- [ ] Recorre de `1` a `argc-1`.
- [ ] Formato idéntico al ejemplo.
### Ejercicio 13.6 — Contador de parámetros extra
**Conceptos** · `argc`, aritmética simple
**Contexto** · Los reportes suelen resumir cuántos datos recibieron. Simplificación: contar los
argumentos extra.
**Consigna** · Escribí un programa que informe **cuántos argumentos extra** (sin contar el nombre)
recibió, con el formato del escenario de prueba.
**Entrada** · Los argumentos pasados al ejecutarlo.
**Salida** · Una línea con la cantidad.
**Escenario de prueba**
```text
$ ./contar a b c d
Recibiste 4 parámetro(s) extra(s).
```
```text
$ ./contar
Recibiste 0 parámetro(s) extra(s).
```
**Identificadores sugeridos** · `argc`, `extraCount`
**Restricciones / pistas**
- La cantidad de extra es `argc - 1`. Los corchetes del ejemplo son literales: no hace falta manejar
plural/singular finamente.
**Criterios de aceptación**
- [ ] Con `a b c d` informa `4`.
- [ ] Sin argumentos informa `0`.
- [ ] El cálculo usa `argc` (no cuenta a mano).
### Ejercicio 13.7 — Clasificador de argumentos
**Conceptos** · `argv[i][0]`, comparación de cadenas, clasificación
**Contexto** · Antes de interpretar opciones, hay que saber qué tipo de argumento es cada uno.
Simplificación: clasificar sin ejecutar ninguna acción.
**Consigna** · Escribí un programa que recorra desde `argv[1]` y clasifique cada argumento como:
- `[OPCION LARGA]` si empieza con `--` y tiene algo después;
- `[OPCION CORTA]` si empieza con **un** `-` y tiene algo después;
- `[POSICIONAL]` si no empieza con `-`.
**Entrada** · Los argumentos pasados al ejecutarlo.
**Salida** · Una línea por argumento con el formato del ejemplo.
**Escenario de prueba**
```text
$ ./clasifica -v --verbose archivo.txt -h
-v -> [OPCION CORTA]
--verbose -> [OPCION LARGA]
archivo.txt -> [POSICIONAL]
-h -> [OPCION CORTA]
```
**Identificadores sugeridos** · `argc`, `argv`, `i`
**Restricciones / pistas**
- Distinguí `--` de `-` mirando el **segundo** carácter.
- Contemplá (aunque sea mínimamente) los casos `"-"` y `"--"` para no clasificarlos mal.
**Criterios de aceptación**
- [ ] Distingue correctamente los tres tipos.
- [ ] No confunde `--` con `-`.
- [ ] Recorre solo los argumentos extra.
- [ ] El formato coincide con el ejemplo.
---
# PARTE 5 — Opciones cortas y largas (`-v` vs `--verbose`)
## 5.1 Dos estilos, una misma idea
Las herramientas de consola usan **dos estilos** para las opciones. No compiten: **conviven**.
| Estilo | Prefijo | Ejemplos | Para qué sirve |
|---|---|---|---|
| **Corta** | `-` (un guión) | `-v`, `-h`, `-r`, `-o` | Rápida de tipear. Casi siempre letra única. |
| **Larga** | `--` (dos guiones) | `--verbose`, `--help`, `--output` | Se lee como palabra. Ideal para no memorizar letras. |
**Analogía.** Es como abreviaturas vs. palabras completas: `tel.` vs. `teléfono`. Las dos sirven; la
completa deja menos dudas.
La buena noticia: casi siempre `-v` y `--verbose` **hacen lo mismo**. Es el mismo botón con dos
etiquetas. Tu trabajo es que las dos etiquetas activen el mismo comportamiento.
## 5.2 Opciones cortas apiladas: `-abc`
Un detalle muy cómodo de las opciones **cortas**: se pueden **apilar**. Escribir:
```bash
./programa -abc
```
es lo mismo que:
```bash
./programa -a -b -c
```
Son **tres** opciones cortas juntas. Esto **solo** aplica a las cortas (`-`); las largas (`--`) nunca
se apilan así.
> **Ojo con esto.** "Apilar" no es gratis: si querés soportarlo **a mano**, tenés que recorrer cada
> carácter del argumento. Por suerte, en la Parte 8 veremos que `getopt` **lo hace por vos**.
## 5.3 El separador `--` (fin de opciones)
Existe una convención poderosa: un argumento que es **solo** `--` le dice al programa
*"de acá en adelante, todo es posicional, aunque empiece con guión"*. Se usa para archivos con nombres
raros:
```bash
./programa -- --archivo-que-empieza-con-guiones.txt
```
Acá `--archivo-...` es un **archivo**, no una opción. Es un caso avanzado; mencionarlo te evita
sorpresas cuando leas el código de otras herramientas.
## 5.4 Detectar una opción con seguridad
Un chequeo básico y seguro (contemplando el caso `"-"`):
```c
if (argv[i][0] == '-' && argv[i][1] != '\0') {
/* Es una opción: arranca con '-' y no es solo "-" */
}
```
Ese `!= '\0'` pregunta "¿hay algo **después** del guión?". Así un `"-"` solito (que suele significar
*"leer de la entrada estándar"*) **no** se confunde con una opción.
## Práctica de la Parte 5
### Ejercicio 13.8 — Simulador de ayuda
**Conceptos** · comparación de cadenas (`strcmp`), opciones cortas y largas, terminación del programa
**Contexto** · Toda herramienta debe poder explicarse sola. `-h` y `--help` son el estándar de facto.
Simplificación: mostrar una ayuda fija y salir.
**Consigna** · Escribí un programa que, si recibe **`-h` o `--help`** (cualquiera de las dos), muestre
un mensaje de ayuda y **termine**. Si no las recibe, no debe mostrar la ayuda.
**Entrada** · Argumentos por línea de comandos.
**Salida** · El mensaje de ayuda (con el nombre del programa incluido), solo si corresponde.
**Escenario de prueba**
```text
$ ./ayuda -h
Uso: ./ayuda [opciones] <archivo>
Opciones:
-h, --help Muestra esta ayuda
-v, --verbose Modo detallado
```
```text
$ ./ayuda --help
Uso: ./ayuda [opciones] <archivo>
Opciones:
-h, --help Muestra esta ayuda
-v, --verbose Modo detallado
```
**Identificadores sugeridos** · `argc`, `argv`, `i`
**Restricciones / pistas**
- Tenés que reconocer **las dos** variantes: `-h` **y** `--help`.
- Para comparar cadenas completas usá `strcmp` (no `==`, que compara direcciones).
- Al mostrar ayuda, el programa debe **terminar**: no sigas procesando.
- La ayuda usa `argv[0]` en la línea `Uso: ...`.
**Criterios de aceptación**
- [ ] Detecta tanto `-h` como `--help`.
- [ ] Son variantes equivalentes (mismo resultado).
- [ ] Tras mostrar la ayuda, el programa **termina**.
- [ ] La línea `Uso:` usa `argv[0]`.
---
# PARTE 6 — Opciones con valor
## 6.1 El problema
Muchas opciones necesitan **un valor**: `--max=10`, `--output=resultado.txt`, `-o salida.txt`. El
valor es el dato que la opción lleva consigo.
Hay **dos formas** de pasarlo, y las dos son válidas:
| Forma | Ejemplo | En `argv` queda... |
|---|---|---|
| **Pegada con `=`** | `--dato=2` | **un** argumento: `"--dato=2"` |
| **Separada por espacio** | `--dato 2` | **dos** argumentos: `"--dato"` y `"2"` |
**Analogía.** Es como decir *"una Coca-Cola grande"* vs. *"una Coca-Cola, grande"*. Mismo significado,
forma distinta.
> **Momento ajá.** Esto es central. Muchos programas aceptan **las dos formas**. Cuando parseás a mano,
> o contemplás ambas, o al menos **documentás** cuál aceptás.
## 6.2 Forma 1: con `=` (todo en un argumento)
Acá el valor vive **dentro** del mismo argumento, después del `=`.
```c
/* ¿el argumento empieza con "--dato="? */
if (strncmp(argv[i], "--dato=", 7) == 0) {
char *valor = argv[i] + 7; /* lo que viene después del '=' */
/* 'valor' apunta a "2" */
}
```
Desarmemos el `+ 7`: `"--dato="` mide **7** caracteres (`-`, `-`, `d`, `a`, `t`, `o`, `=`). Sumar 7 al
puntero lo corre justo **hasta el primer carácter del valor**. Eso es **aritmética de punteros**: si
`argv[i]` apunta al inicio de la cadena, `argv[i] + 7` apunta al octavo carácter.
> **Para guardar.** `strncmp` compara solo los primeros `n` caracteres. Lo usamos para preguntar
> "¿empieza con `--dato=`?" sin importar qué sigue. Después tomamos el resto con `+ 7`.
## 6.3 Forma 2: separada por espacio (valor en el argumento siguiente)
Acá el valor está en **el siguiente** elemento de `argv`.
```c
/* ¿el argumento es exactamente "--dato" y hay un siguiente? */
if (strcmp(argv[i], "--dato") == 0 && (i + 1) < argc) {
char *valor = argv[i + 1]; /* el valor es el argumento siguiente */
i++; /* ¡salto el valor para no reprocesarlo! */
/* usar 'valor' */
}
```
Los dos detalles que importan:
1. **`(i + 1) < argc`**: comprobar que **existe** un argumento siguiente. Si no, el usuario escribió
`--dato` al final y **falta** el valor.
2. **`i++`**: avanzar un paso extra para que el bucle **no vuelva a procesar** el valor `"2"` como si
fuera un argumento independiente.
> **Trampa N.º 3 — comerse el argumento siguiente.** Si te olvidás del `i++`, en la próxima vuelta del
> bucle vas a ver `"2"` y lo vas a clasificar como **posicional** (o peor). El valor de una opción
> **no** es un argumento suelto: es parte de la opción. Avanzá un lugar.
## 6.4 ¿Y si el valor es un número?
`argv` siempre guarda **texto**. Si la opción representa un número, hay que **convertirlo**:
- `atoi("42")` → `42` (cómodo, pero no avisa si la cadena **no** era un número).
- `strtol("42", &fin, 10)` → más robusto: te deja **verificar** que toda la cadena se convirtió.
- `strtod` para números con decimales.
Elegir bien la conversión es parte de la Parte 7 (robustez).
## 6.5 Detectar que falta el valor
Si el usuario escribe `--dato` **y no pone** el valor (forma separada), tenés que **detectarlo** y
avisar. Ese chequeo es el `(i + 1) < argc` del ejemplo: si es falso, el valor **falta**. Ahí se muestra
un error claro y, normalmente, se termina con código de error (Parte 7).
## Práctica de la Parte 6
### Ejercicio 13.9 — Opción con valor (`--max=N`)
**Conceptos** · `strncmp`, `atoi`, aritmética de punteros
**Contexto** · Muchas herramientas aceptan límites configurables. Simplificación: leer un máximo.
**Consigna** · Escribí un programa que detecte la opción `--max=N` (forma pegada con `=`). Si está
presente, muestra `Máximo: N`. Si no está, muestra `Máximo: sin definir`.
**Entrada** · Argumentos por línea de comandos.
**Salida** · La línea del ejemplo.
**Escenario de prueba**
```text
$ ./limite --max=10
Máximo: 10
```
```text
$ ./limite
Máximo: sin definir
```
**Identificadores sugeridos** · `argc`, `argv`, `i`, `maxValue`
**Restricciones / pistas**
- Detectá el prefijo `--max=` con `strncmp`.
- Extraé el valor sumando al puntero la longitud del prefijo.
- Si no aparece la opción, el valor queda "sin definir" (una bandera booleana te ayuda).
**Criterios de aceptación**
- [ ] Detecta la forma con `=`.
- [ ] Muestra el valor correcto.
- [ ] Sin la opción, muestra `sin definir`.
- [ ] No crashea si el programa se ejecuta sin argumentos.
### Ejercicio 13.10 — Opción con valor separado (`--max 10`)
**Conceptos** · `strcmp`, índice siguiente, `i++`
**Contexto** · Otra convención muy común: opción y valor separados por espacio. Simplificación: el
mismo máximo, ahora en dos argumentos.
**Consigna** · Igual que 13.9, pero aceptando la **forma separada por espacio**: `--max 10`.
**Entrada** · Argumentos por línea de comandos.
**Salida** · La misma que 13.9.
**Escenario de prueba**
```text
$ ./limite --max 10
Máximo: 10
```
**Identificadores sugeridos** · `argc`, `argv`, `i`, `maxValue`
**Restricciones / pistas**
- Compará la cadena completa con `strcmp`.
- Verificá que exista el argumento siguiente (`i + 1 < argc`).
- No olvides el `i++` para saltar el valor.
**Criterios de aceptación**
- [ ] Acepta la forma separada por espacio.
- [ ] Salta el valor consumido (`i++`).
- [ ] Verifica que el valor exista antes de usarlo.
### Ejercicio 13.11 — Aceptar las dos formas
**Conceptos** · combinación de `strncmp` y `strcmp`, control de flujo
**Contexto** · Las herramientas profesionales aceptan múltiples formas de escribir lo mismo.
Simplificación: unir la forma pegada y la separada.
**Consigna** · Mejorá 13.9 y 13.10 en **un solo** programa que acepte **tanto `--max=10` como
`--max 10`**.
**Entrada** · Argumentos por línea de comandos.
**Salida** · Igual que 13.9.
**Escenario de prueba**
```text
$ ./limite --max=10
Máximo: 10
```
```text
$ ./limite --max 10
Máximo: 10
```
**Identificadores sugeridos** · `argc`, `argv`, `i`, `maxValue`, `haveMax`
**Restricciones / pistas**
- Podés preguntar primero por el prefijo `--max=` y, si no coincide, por la igualdad exacta
`--max`.
- Evitá duplicar la conversión: una sola vez que tengas el texto del valor, convertilo.
**Criterios de aceptación**
- [ ] Funciona con `=` y con espacio.
- [ ] No hay comportamiento distinto entre las dos formas (mismo resultado).
- [ ] El código no repite innecesariamente la lógica de conversión.
### Ejercicio 13.12 — Detectar valor faltante
**Conceptos** · control de errores, `stderr`, terminación con código
**Contexto** · Un programa serio no se cuelga ni crashea si al usuario se le olvida el valor. Avisa y
termina. Simplificación: error de argumento incompleto.
**Consigna** · Partiendo de 13.10, si el usuario escribe `--max` **sin valor** (al final de la línea),
el programa debe mostrar un error **y terminar**. No debe seguir como si nada.
**Entrada** · Argumentos por línea de comandos.
**Salida** · Mensaje de error y línea de uso.
**Escenario de prueba**
```text
$ ./limite --max
Error: la opción --max necesita un valor
Uso: ./limite [--max=<n> | --max <n>]
```
**Identificadores sugeridos** · `argc`, `argv`, `i`, `ok`
**Restricciones / pistas**
- El caso es `i + 1 >= argc` cuando la opción es exactamente `--max`.
- El mensaje de error va por la **salida de errores** (`stderr`), no por la salida normal.
- Terminá el programa con un código **distinto de 0** para señalar el fallo (Parte 7).
**Criterios de aceptación**
- [ ] Detecta `--max` sin valor.
- [ ] Muestra un error claro (sin crashear).
- [ ] Incluye una línea de uso.
- [ ] Termina con código distinto de 0.
---
# PARTE 7 — Validar, avisar y salir con estilo
## 7.1 La regla de oro
**"Fracasar con claridad es mejor que romperse en silencio."**
Un buen programa de consola, ante una entrada inválida:
1. **Avisa** qué pasó, en un mensaje claro;
2. **Muestra** cómo se usa;
3. **Termina** con un código que indique el error.
## 7.2 Códigos de salida
El `return` de `main` es lo que el **shell** (y cualquier script) ve como **código de salida**:
- **`0`** → éxito (es el único que significa "todo bien").
- **Distinto de 0** → error (`1`, `2`, etc.).
Por eso, cuando algo falla: `return 1;`. Quien ejecutó tu programa puede **decidir** en función de eso
(un `if` en un script, un `&&` en la terminal, una tarea automatizada).
> **Momento ajá.** El código de salida **no** es un capricho: es la forma en que un programa le
> **cuenta** a otro si salió bien. Es la versión "de consola" de un booleano.
## 7.3 Errores por `stderr`, resultados por `stdout`
Esto es un detalle **profesional** que marca la diferencia:
- **Resultados normales** → `stdout` (`printf`).
- **Errores, avisos y ayudas** → `stderr` (`fprintf(stderr, ...)`).
**¿Por qué?** Porque si alguien guarda la salida normal en un archivo:
```bash
./programa datos.txt > resultado.txt
```
los **errores no deben** colarse en `resultado.txt`. `stdout` va al archivo; `stderr` **sigue
apareciendo** en la pantalla, separado. Los datos siguen limpios.
```c
fprintf(stderr, "Error: no se encontró el archivo '%s'\n", nombre);
```
> **Puente al capítulo siguiente.** `stdin`, `stdout` y `stderr` son las **tres tuberías** de todo
> programa. Acá las usamos apenas; las vas a entender a fondo en
> `14-entrada-salida-estandar`. Por ahora, quedate con la regla: **errores a `stderr`, resultados a
> `stdout`**.
## 7.4 El mensaje de uso (`--help`)
Toda herramienta que acepta opciones debería tener `-h`/`--help`. Debe ser **conciso, claro** y usar
`argv[0]`. Un buen formato de referencia:
```text
Uso: ./programa [opciones] <archivo_entrada> [archivo_salida]
Opciones:
-h, --help Muestra esta ayuda
-v, --verbose Activa modo detallado
-o, --output <file> Especifica archivo de salida
--max <n> Máximo de elementos (por defecto: 100)
```
La convención de `[]` y `<>` es vieja y muy útil:
- `<>` → **obligatorio** (`<archivo_entrada>`).
- `[]` → **opcional** (`[archivo_salida]`, `[opciones]`).
## 7.5 Validar argumentos obligatorios
Si tu programa **necesita** un archivo, comprobá que esté:
```c
if (argc < 2) {
fprintf(stderr, "Error: falta el archivo de entrada\n");
fprintf(stderr, "Uso: %s <archivo>\n", argv[0]);
return 1;
}
```
Ese `argc < 2` significa: "solo vino el nombre del programa, no hay ningún dato".
## Práctica de la Parte 7
### Ejercicio 13.13 — Archivo obligatorio + ayuda
**Conceptos** · `argc`, `strcmp`, `stderr`, códigos de salida, validación
**Contexto** · Un procesador de archivos real exige un archivo y ofrece ayuda. Simplificación: la
misma estructura, sin procesar nada de verdad.
**Consigna** · Creá un programa que:
1. Si recibe `-h` o `--help` → muestre la ayuda y retorne `0`.
2. Si **no** recibe ningún argumento extra → muestre un error por `stderr`, la línea de uso, y
retorne `1`.
3. Si recibe un nombre de archivo (posicional) → muestre `Procesando: <archivo>` por `stdout` y
retorne `0`.
**Entrada** · Argumentos por línea de comandos.
**Salida** · Según el caso, ayuda, error o confirmación.
**Escenario de prueba**
```text
$ ./procesar --help
Uso: ./procesar [opciones] <archivo>
Opciones:
-h, --help Muestra esta ayuda
```
```text
$ ./procesar
Error: se requiere un archivo
Uso: ./procesar [opciones] <archivo>
```
```text
$ ./procesar datos.txt
Procesando: datos.txt
```
**Identificadores sugeridos** · `argc`, `argv`, `i`, `fileName`
**Restricciones / pistas**
- La ayuda tiene **prioridad**: detectala **antes** de validar el obligatorio.
- Los **errores** van a `stderr`; la confirmación normal a `stdout`.
- Usá `argv[0]` en los mensajes de uso.
**Criterios de aceptación**
- [ ] La ayuda se muestra con `-h` y con `--help`, y retorna `0`.
- [ ] Sin argumentos, el error va a `stderr` y retorna `1`.
- [ ] Con archivo, confirma por `stdout` y retorna `0`.
- [ ] Los mensajes usan `argv[0]`.
- [ ] Los códigos de salida son los correctos.
### Ejercicio 13.14 — Validar que el valor sea numérico
**Conceptos** · `strtol`/`isdigit`, validación, `stderr`
**Contexto** · `--max=abc` no tiene sentido: el máximo debe ser un número. El programa debe avisarlo.
Simplificación: validación de un argumento numérico.
**Consigna** · Ampliá el programa del 13.11: si `--max` recibe un valor que **no** es un entero válido,
mostrá un error y terminá con código de error. Si es válido, mostrá el máximo.
**Entrada** · Argumentos por línea de comandos.
**Salida** · El máximo o un error.
**Escenario de prueba**
```text
$ ./limite --max=abc
Error: '--max' requiere un número entero
```
```text
$ ./limite --max=42
Máximo: 42
```
**Identificadores sugeridos** · `maxText`, `maxValue`, `end`
**Restricciones / pistas**
- Una forma robusta: `strtol` y comprobar que **consumió toda** la cadena (`*end == '\0'`) y que no
hubo desbordamiento.
- El error va a `stderr` y el programa retorna distinto de 0.
- **No** uses `atoi` solo: no distingue `"42"` de `"42abc"` ni de `"abc"`.
**Criterios de aceptación**
- [ ] `--max=abc` produce un error claro por `stderr`.
- [ ] `--max=42` muestra `Máximo: 42`.
- [ ] Un valor parcialmente numérico (`--max=42x`) se rechaza.
- [ ] El programa no crashea y retorna el código correcto.
---
# PARTE 8 — Orden y robustez: `getopt`
## 8.1 Por qué existe
Parsear a mano es **perfecto para aprender** (y lo que hiciste hasta acá lo prueba). Pero cuando hay
**muchas** opciones, o querés soportar `-abc` apiladas y detectar faltantes de forma consistente, el
código a mano se vuelve repetitivo y frágil.
Para eso existe **`getopt()`**, una función estándar (POSIX) que hace ese trabajo por vos.
> **Para entender antes de usar.** **No saltees las Partes 4 a 7.** Dominar el parseo manual es lo que
> te permite entender **qué hace** `getopt` por dentro, y detectar errores cuando algo no cuadra.
## 8.2 Qué resuelve `getopt`
- Maneja opciones **cortas** (`-v`, `-h`, `-o valor`).
- Soporta opciones **apiladas** (`-abc` = `-a -b -c`).
- Sabe cuáles opciones **requieren un valor** y cuáles no.
- Avisa de **opciones desconocidas** y de **valores faltantes**.
- Deja los **posicionales juntos** al final, en `optind` (te lo explico en 8.4).
## 8.3 La forma de usarlo
```c
#include <unistd.h> /* getopt */
#include <stdio.h>
int main(int argc, char *argv[])
{
int opt;
/* optstring: letras de opciones cortas.
Un ':' DESPUÉS de una letra significa "esa opción requiere un valor". */
while ((opt = getopt(argc, argv, "hvf:o:")) != -1) {
switch (opt) {
case 'h': /* -h sin valor */ break;
case 'v': /* -v sin valor */ break;
case 'f': /* -f <archivo> */ break;
case 'o': /* -o <archivo> */ break;
case '?': /* opción desconocida o valor faltante */ break;
default: break;
}
}
return 0;
}
```
Desarmemos el `"hvf:o:"`:
| Letra en `optstring` | Significa |
|---|---|
| `h` | opción `-h` (sin valor) |
| `v` | opción `-v` (sin valor) |
| `f:` | opción `-f` que **requiere** un valor (el `:` va **después** de la letra) |
| `o:` | opción `-o` que **requiere** un valor |
El valor de una opción con `:` queda en la variable global **`optarg`** (`char *`).
> **Momento ajá.** El `:` va **a la derecha** de la letra que necesita argumento. Leer `"f:"` es decir
> "la opción `-f` lleva un valor". Leer `"hf:"` es decir "`-h` sin valor, `-f` con valor".
## 8.4 Los posicionales después de `getopt`
Cuando `getopt` termina (devuelve `-1`), todas las opciones ya se procesaron. La variable global
**`optind`** apunta al **primer argumento que no es opción**. De ahí en adelante, todo es posicional:
```c
while ((opt = getopt(argc, argv, "hv")) != -1) { /* ... */ }
for (int i = optind; i < argc; i++) {
printf("Posicional: %s\n", argv[i]);
}
```
`getopt` **reordena** internamente la línea para dejarte las opciones primero y los posicionales
después. Vos solo leés desde `optind`.
## 8.5 Errores que detecta
- **Opción desconocida:** `getopt` devuelve `'?'` (y suele imprimir un aviso por `stderr`).
- **Valor faltante:** si una opción con `:` aparece sin su valor, también se señala con `'?'` (o `:`
según configuración). Lo habitual es tratar `'?'` mostrando tu propio mensaje de uso, más claro para
el lector.
## 8.6 Disponibilidad
`getopt` viene de `<unistd.h>`, típico de Linux y macOS. En Windows, con MinGW/GCC o entornos POSIX
(como Git Bash / MSYS2), suele estar disponible. Si compilás con MSVC puro, no; ahí este capítulo te
sirve igual porque ya sabés hacerlo a mano.
## Práctica de la Parte 8
### Ejercicio 13.15 — `getopt` básico
**Conceptos** · `getopt`, `optstring`, `switch`, opciones apiladas
**Contexto** · Reescribir el parseo con la función estándar. Simplificación: tres opciones cortas.
**Consigna** · Usando `getopt`, interpretá:
- `-h` → mostrá la ayuda y terminá;
- `-v` → activá modo detallado (informá `Modo verbose: ON`);
- `-f <archivo>` → requiere un archivo (`Archivo: <archivo>`).
**Entrada** · Argumentos por línea de comandos.
**Salida** · Según las opciones recibidas.
**Escenario de prueba**
```text
$ ./prog -v -f datos.txt
Modo verbose: ON
Archivo: datos.txt
```
```text
$ ./prog -vf datos.txt
Modo verbose: ON
Archivo: datos.txt
```
**Identificadores sugeridos** · `opt`, `optstring`, `optarg`
**Restricciones / pistas**
- El `optstring` correcto para esto es `"hvf:"`.
- El segundo escenario prueba que **`-vf` apiladas** funciona sin código extra (lo hace `getopt`).
- Para `-h`, terminá el programa dentro del `case`.
**Criterios de aceptación**
- [ ] Usa `getopt` con `optstring` correcto.
- [ ] Acepta `-v` y `-f` juntas (`-vf`).
- [ ] `-f` toma su valor de `optarg`.
- [ ] `-h` muestra ayuda y termina.
### Ejercicio 13.16 — Posicionales con `getopt`
**Conceptos** · `optind`, posicionales, orden de argumentos
**Contexto** · Separar las opciones de los datos "de verdad". Simplificación: listar los archivos
sueltos después de las opciones.
**Consigna** · Ampliá 13.15 para que, **después** de procesar las opciones, muestre todos los
posicionales restantes (los que quedan desde `optind`).
**Entrada** · Mezcla de opciones y posicionales.
**Salida** · El efecto de las opciones y luego la lista de posicionales.
**Escenario de prueba**
```text
$ ./prog -v entrada.txt salida.txt extra.txt
Modo verbose: ON
Posicionales:
entrada.txt
salida.txt
extra.txt
```
**Identificadores sugeridos** · `opt`, `optind`, `i`
**Restricciones / pistas**
- Recorré desde `optind` hasta `argc-1`.
- No mezcles: las opciones se procesan en el `while`; los posicionales, **después**.
**Criterios de aceptación**
- [ ] Usa `optind` para ubicar el primer posicional.
- [ ] Lista todos los posicionales en orden.
- [ ] Las opciones no se mezclan con la lista.
---
# PARTE 9 — Nivel pro: `getopt_long`
## 9.1 El salto
Ahora sí, **nivel pro**: opciones **largas** (`--verbose`, `--help`, `--output=...`, `--max 10`).
Para eso existe **`getopt_long()`** (también POSIX, header `<getopt.h>`). Es **el estándar** que usan
herramientas como `ls`, `grep` y `gcc`. Todo lo que aprendiste de `getopt` sigue valiendo:
`getopt_long` es su hermano mayor, que además entiende las palabras largas.
## 9.2 Comparación rápida
| Característica | `getopt` | `getopt_long` |
|---|---|---|
| Opciones cortas (`-v`) | Sí | Sí |
| Opciones largas (`--verbose`) | **No** | **Sí** |
| Acepta `--opcion=valor` | No | **Sí** |
| Acepta `--opcion valor` | No | **Sí** |
| Manejo de `-abc` apiladas | Sí | Sí |
## 9.3 Las tres piezas
Para usar `getopt_long` necesitás **tres** cosas:
1. incluir `<getopt.h>`;
2. declarar una **tabla** de opciones largas (`struct option`);
3. llamar a **`getopt_long`** en lugar de `getopt`.
## 9.4 La tabla de opciones largas
```c
#include <getopt.h>
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 obligatorio */
};
```
Cada fila tiene **cuatro columnas**:
1. **Nombre largo** (`"help"`) → se reconoce como `--help`.
2. **¿Lleva valor?** → `no_argument` (flag) o `required_argument` (con valor).
3. **¿A dónde apunta el resultado?** → `0` significa "devolvé el valor de la cuarta columna".
4. **Código corto asociado** → `'h'`, `'v'`, `'o'`, `'m'`. Así la opción larga **y** la corta caen en
el **mismo `case`** del `switch`.
La última fila `{0,0,0,0}` es el **terminador**: le dice a `getopt_long` dónde acaba la tabla. **No la
olvides** (es el bug clásico de esta función).
> **Momento ajá.** Gracias a la cuarta columna, `--verbose` y `-v` comparten el `case 'v'`. Escribís
> el comportamiento **una sola vez** y las dos formas lo activan. Eso es elegancia, no magia.
## 9.5 Cómo se llama
```c
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 (optarg) */break;
case 'm': /* -m o --max (optarg) */ break;
case '?': /* desconocida / falta valor */ break;
default: break;
}
}
```
El primer string (`"hvo:m:"`) sigue siendo el `optstring` de las opciones **cortas**. La tabla es para
las **largas**. Y `getopt_long` entiende, sin costo extra:
- `--output=archivo.txt` (pegado con `=`);
- `--output archivo.txt` (separado por espacio).
Para las largas, el valor llega —igual que siempre— en **`optarg`**.
## 9.6 Errores
- Opción larga **desconocida** → `'?'`;
- Opción larga que **requiere valor** y no lo tiene → `'?'`;
- Opción corta **desconocida** → `'?'`.
Como en `getopt`, lo más limpio es manejar `'?'` mostrando **tu** mensaje de uso y retornando el
código de error.
## Práctica de la Parte 9
### Ejercicio 13.17 — (JEFE) Utilidad de consola completa
**Conceptos** · `getopt_long`, `struct option`, `optarg`, `optind`, `stderr`, códigos de salida,
validación numérica, ayuda.
**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:
| 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:
1. Acepte las **dos formas** de valor para las largas (`--output=x` y `--output x`).
2. Trate los argumentos **no-opción** como archivos posicionales (listados al final).
3. Valide que `--max` sea un entero; si no, error por `stderr` y retorno distinto de 0.
4. Ante una opción desconocida, muestre un error por `stderr`, la línea de uso y retorne distinto de 0.
5. 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**
```text
$ ./util --verbose --max=10 entrada.txt salida.txt
Modo verbose: ON
Máximo: 10
Posicionales:
entrada.txt
salida.txt
```
```text
$ ./util --max
Error: la opción --max necesita un valor
Uso: ./util [opciones] <archivo>...
```
```text
$ ./util --max=abc
Error: '--max' requiere un número entero
```
```text
$ ./util --desconocida
Error: opción desconocida
Uso: ./util [opciones] <archivo>...
```
**Identificadores sugeridos** · `opt`, `long_options`, `option_index`, `optind`, `verbose`,
`max_value`, `output_name`, `have_output`, `i`
**Restricciones / pistas**
- Usá `getopt_long` con el `optstring` `"hvo:m:"` (o el que corresponda a tus short options).
- **No** olvides el terminador `{0,0,0,0}` de la tabla.
- El `-h` tiene **prioridad**: mostralo apenas aparece y terminá.
- Validá `--max` con `strtol` comprobando que se consumió **toda** la cadena.
- Errores → `stderr`; resultados → `stdout`.
- Los posicionales salen de `argv[optind..argc-1]`.
**Criterios de aceptación**
- [ ] Reconoce `-v`/`--verbose`, `-o`/`--output` y `-m`/`--max`.
- [ ] Acepta valor pegado (`--max=10`) y separado (`--max 10`).
- [ ] `--help`/`-h` muestra ayuda y retorna `0`.
- [ ] `--max` sin valor y `--max=abc` producen errores claros por `stderr` con código de retorno != 0.
- [ ] Una opción desconocida produce error por `stderr` y código != 0.
- [ ] Los posicionales se listan después de las opciones (usando `optind`).
- [ ] Los resultados van a `stdout` y los errores a `stderr`.
- [ ] No hay accesos fuera de rango ni bucles infinitos.
---
## Cierre: lo que te llevás
Si tuvieras que quedarte con **una sola idea**, que sea esta: **tu programa puede recibir sus órdenes
al nacer**, y esas órdenes llegan como una lista de cadenas que se llama `argv`.
- **`argc` cuenta, `argv` guarda.** `argc` es cuántos argumentos hay (incluido el nombre); `argv` es la
lista, con `argv[0]` = el nombre del programa.
- **El espacio separa; las comillas agrupan.** `"hola mundo"` entre comillas es **un** argumento.
- **Recorré con `i < argc`.** Nunca toques `argv[argc]`: es la trampa N.º 1.
- **Aprendé a clasificar:** posicional (sin `-`), opción corta (`-x`), opción larga (`--xx`), y opción
con valor (`--xx=valor` o `--xx valor`).
- **Ojo con la trampa N.º 3:** cuando el valor viene separado por espacio, avanzá un índice (`i++`)
para no reprocesarlo.
- **Fracasá con claridad:** errores por `stderr`, ayuda con `argv[0]`, y `return` distinto de 0 ante
error.
- **Dominá lo manual primero** (Partes 4–7); recién después **delegate** en `getopt` y `getopt_long`,
que te dan orden, opciones apiladas y soporte de `--largas` con valor.
Y el puente hacia lo que viene: hasta ahora viste **cómo entra la orden** (por la línea de comandos).
En `14-entrada-salida-estandar` vas a aprender **cómo entra y sale todo lo demás**: las tres tuberías
`stdin`/`stdout`/`stderr`, la lectura por teclado, los búferes y `EOF`. Los argumentos te dijeron qué
hacer; el capítulo siguiente te enseña a **conversar** con el programa mientras corre.