StackPractices
beginner Por Mathias Paulenko

Analizar Argumentos CLI: argparse, Commander y picocli

Cómo analizar argumentos de línea de comandos en aplicaciones CLI de Python, Java y Node.js.

Temas: data

Visión General

El análisis de argumentos de línea de comandos es lo primero que ven los usuarios al ejecutar tu herramienta. Una buena CLI expone flags claros, opciones tipadas, texto de ayuda automático y subcomandos que se sienten como git push o docker run.

Mermaid flowchart LR diagram

Cuándo Usar

Usá esto cuando construyas herramientas de desarrollo, scripts de build, automatización de deployment o pipelines de datos que necesiten entradas configurables. También sirve para interfaces basadas en subcomandos o cuando hard-codear valores haría que un script sea más difícil de reutilizar.

Requisitos Previos

  • Python: 3.8+ para argparse (librería estándar). Click requiere pip install click. typer requiere pip install typer.
  • Node.js: 16+ para commander (imports ESM). Instalá con npm install commander. yargs es una alternativa (npm install yargs).
  • Java: 8+ para picocli. Agregá la dependencia Maven info.picocli:picocli:4.7.6 o la equivalente de Gradle.

Solución

Python

# argparse es la librería estándar para CLI en Python
import argparse

parser = argparse.ArgumentParser(description='Procesar archivos.')
parser.add_argument('input', help='Ruta del archivo de entrada')
parser.add_argument('-o', '--output', default='out.txt', help='Ruta del archivo de salida')
parser.add_argument('-v', '--verbose', action='store_true', help='Activar logging detallado')

args = parser.parse_args()
print(f'Input: {args.input}, Output: {args.output}, Verbose: {args.verbose}')
# Click es una alternativa popular de terceros
# pip install click
import click

@click.command()
@click.argument('input')
@click.option('--output', '-o', default='out.txt', help='Archivo de salida')
@click.option('--verbose', '-v', is_flag=True, help='Modo detallado')
def cli(input, output, verbose):
    click.echo(f'Input: {input}, Output: {output}, Verbose: {verbose}')

if __name__ == '__main__':
    cli()
# Subcomandos con argparse (como git push / git pull)
import argparse

parser = argparse.ArgumentParser(prog='mytool')
sub = parser.add_subparsers(dest='command', required=True)

# subcomando push
push = sub.add_parser('push', help='Enviar datos al remoto')
push.add_argument('--force', action='store_true', help='Forzar envío')

# subcomando pull
pull = sub.add_parser('pull', help='Bajar datos del remoto')
pull.add_argument('--depth', type=int, default=1, help='Profundidad de clonado')

args = parser.parse_args()
print(f'Comando: {args.command}, Force: {getattr(args, "force", False)}')

JavaScript

// process.argv integrado de Node.js es el array raw
const args = process.argv.slice(2);
console.log(args);
// Commander.js es el framework CLI más popular para Node.js
// npm install commander
import { Command } from 'commander';
const program = new Command();

program
  .argument('<input>', 'Ruta del archivo de entrada')
  .option('-o, --output <file>', 'Ruta del archivo de salida', 'out.txt')
  .option('-v, --verbose', 'Activar logging detallado')
  .action((input, options) => {
    console.log(`Input: ${input}, Output: ${options.output}, Verbose: ${options.verbose}`);
  });

program.parse();

Java

// picocli es el estándar moderno para CLI en Java
// Maven: info.picocli:picocli
import picocli.CommandLine;
import picocli.CommandLine.Parameters;
import picocli.CommandLine.Option;
import java.util.concurrent.Callable;

@CommandLine.Command(name = "process", mixinStandardHelpOptions = true)
public class ProcessFile implements Callable<Integer> {
    @Parameters(index = "0", description = "Ruta del archivo de entrada")
    private String input;

    @Option(names = {"-o", "--output"}, defaultValue = "out.txt")
    private String output;

    @Option(names = {"-v", "--verbose"})
    private boolean verbose;

    @Override
    public Integer call() {
        System.out.printf("Input: %s, Output: %s, Verbose: %b%n", input, output, verbose);
        return 0;
    }

    public static void main(String[] args) {
        int exitCode = new CommandLine(new ProcessFile()).execute(args);
        System.exit(exitCode);
    }
}

Explicación

Los frameworks CLI toman el array de argumentos raw (sys.argv, process.argv o String[] args) y lo convierten en valores tipados. Generan automáticamente texto de ayuda, validan argumentos requeridos y convierten valores como --count 5 a enteros. También se encargan de flags booleanos, argumentos posicionales, inputs variádicos y subcomandos.

argparse de Python viene con la librería estándar y cubre la mayoría de los scripts. Click usa decoradores y es más fácil de componer. En JavaScript, commander es la opción más común, con configuración encadenable. picocli usa anotaciones de Java y funciona bien con compilación a native-image de GraalVM para CLIs de arranque rápido.

Un patrón común es combinar argumentos CLI con archivos de configuración. Por ejemplo, una herramienta puede aceptar un flag --config que apunta a un archivo YAML o TOML, y luego sobrescribir valores del archivo con flags de línea de comandos. Ver Parsear Archivos YAML en Python y JavaScript y Parsear Archivos TOML en Python y Java para ejemplos de lectura de config files que combinan bien con parsing CLI.

Si tu CLI procesa entrada estructurada, puede que también quieras validarla contra un schema antes de ejecutar. Mirá Validar JSON Schema en Python y JavaScript para patrones de validación de schema que funcionan junto al parsing de argumentos.

Variantes

TecnologíaLibreríaEnfoqueNotas
PythonargparseLibrería estándarCero dependencias, ayuda auto-generada
PythonClickDecoradoresComponible, soporta barras de progreso y prompts
PythontyperType hintsConstruido sobre Click, usa anotaciones Python 3.6+
JavaScriptcommanderAPI FluentMás popular, soporta subcomandos
JavaScriptyargsCadena middlewareAltamente extensible, bueno para CLIs complejos
JavapicocliAnotacionesScripts de autocompletado, soporte native-image
JavaApache Commons CLIPatrón BuilderMás antiguo pero ampliamente usado en enterprise

Buenas Prácticas

Usá librerías estándar primero (argparse, process.argv) para scripts simples y evitar bloat de dependencias. Agregá flags -h y --help a toda CLI, porque los frameworks generan esto automáticamente. Validá argumentos requeridos temprano y mostrá mensajes amigables, no stack traces.

Soportá --version para que usuarios y pipelines de CI/CD puedan fijar versiones de herramientas. Salí con código 0 en caso de éxito y uno distinto de cero ante fallas, así los shell scripts pueden detectar cuando algo falló.

Errores Comunes

Parsear process.argv manualmente en lugar de usar un framework conduce a código frágil y no mantenible. Cuando faltan argumentos requeridos, los usuarios deberían ver el texto de ayuda, no un stack trace.

Mutar estado global en handlers de CLI dificulta el testing y la composición. Ignorar códigos de salida significa que los pipelines de CI/CD no pueden detectar fallas si la CLI siempre sale con 0. Sobre-ingenieriar subcomandos también es común: un script simple con flags suele ser más simple que una CLI multinivel.

Cuándo No Usar

Si un script solo necesita una entrada fija, una CLI es innecesaria; usá una función o una variable de entorno. Si una herramienta solo la ejecutan otros scripts, un archivo de configuración o variables de entorno pueden ser más limpios que flags. Para hot paths críticos en rendimiento donde el overhead del parsing importe, considerá opciones pre-parseadas o compiladas.

Resumen

  • Usá argparse para scripts Python sin dependencias; pasá a Click cuando necesites composición y decoradores.
  • commander es la opción estándar para CLIs de Node.js, con yargs como alternativa más extensible.
  • picocli es el estándar moderno de Java, con config basada en anotaciones y soporte native-image de GraalVM.
  • Siempre agregá flags --help y --version; los frameworks los generan automáticamente.
  • Salí con 0 en éxito y distinto de cero en fallos para que los pipelines de CI/CD detecten problemas.
  • Combiná flags CLI con archivos de config (YAML, TOML) para herramientas que necesiten config persistente o compleja.
  • Código companion completo con tests: stack-practices-resources.

See Also

Preguntas frecuentes

¿Cómo manejo variables de entorno junto con argumentos CLI?

Usá librerías que soporten fallbacks de variables de entorno. Click expone envvar= y picocli expone defaultValue = "${ENV_VAR}". Las variables de entorno son ideales para secretos y valores específicos de deployment que no deberían aparecer en el historial de shell.

¿Cuál es la mejor forma de testear aplicaciones CLI?

Invocá el punto de entrada de la CLI como una función en lugar de spawnear subprocesos. Click de Python tiene runner.invoke(), picocli tiene CommandLine.execute() in-process, y commander se puede testear llamando a .parse() con un array argv simulado. Eso es mucho más rápido que testing basado en shell.

¿Cómo construyo una CLI con subcomandos?

Todos los frameworks principales soportan subcomandos. En argparse, usá add_subparsers(). En commander, llamá a .command() para cada subcomando. En picocli, anotá clases anidadas con @Command. Poné las opciones compartidas en una clase padre o mixin para evitar duplicación.

¿Cómo valido tipos de argumentos más allá de lo que ofrece el framework?

argparse soporta type=int o callables custom. Click usa click.IntRange o subclasses custom de ParamType. commander acepta una función de parse como tercer argumento de .option(). picocli usa @Option(type=...) con implementaciones de ITypeConverter built-in o custom. Validá temprano y fallá con un mensaje de error claro.

¿Qué códigos de salida debería usar?

Seguí la convención POSIX: 0 para éxito, 1 para errores generales, 2 para errores de parsing de argumentos. argparse sale con 2 por defecto en fallos de parse. picocli retorna el valor de call() como exit code. commander setea process.exitCode en lugar de llamar process.exit() directamente, lo que deja que los handlers async terminen primero.

¿Cómo agrego autocomplete a mi CLI?

argparse no soporta autocomplete nativamente, pero argcomplete lo agrega con un solo decorador. Click genera scripts de completion vía click.completion. commander soporta completion con el plugin commander-completion. picocli genera scripts de completion con picocli.AutoComplete.