Parse Command Line Arguments: argparse, Commander & picocli
How to parse command line arguments in Python, Java, and Node.js CLI applications.
Overview
Argument parsing is the first thing users hit when they run your tool. A good CLI exposes clear flags, typed options,
automatic help text, and subcommands that feel like git push or docker run.
When to Use
Reach for a CLI framework when your script needs configurable inputs — build tools, deployment scripts, data pipelines,
or anything where hard-coding values would make reuse painful. Subcommand-style interfaces (like git push vs
git pull) also fit here.
Prerequisites
- Python 3.8+ —
argparseis part of the standard library, no install needed. ForClick, runpip install click; fortyper,pip install typer. - Node.js 16+ — install
commanderwithnpm install commander, or grabyargs(npm install yargs) if you need something more extensible. - Java 8+ — add
info.picocli:picocli:4.7.6to your Mavenpom.xmlor the Gradle equivalent.
Solution
Python
# argparse is the standard library for Python CLI
import argparse
parser = argparse.ArgumentParser(description='Process some files.')
parser.add_argument('input', help='Input file path')
parser.add_argument('-o', '--output', default='out.txt', help='Output file path')
parser.add_argument('-v', '--verbose', action='store_true', help='Enable verbose logging')
args = parser.parse_args()
print(f'Input: {args.input}, Output: {args.output}, Verbose: {args.verbose}')
# Click is a popular third-party alternative
# pip install click
import click
@click.command()
@click.argument('input')
@click.option('--output', '-o', default='out.txt', help='Output file')
@click.option('--verbose', '-v', is_flag=True, help='Verbose mode')
def cli(input, output, verbose):
click.echo(f'Input: {input}, Output: {output}, Verbose: {verbose}')
if __name__ == '__main__':
cli()
# Subcommands with argparse (like git push / git pull)
import argparse
parser = argparse.ArgumentParser(prog='mytool')
sub = parser.add_subparsers(dest='command', required=True)
# push subcommand
push = sub.add_parser('push', help='Push data to remote')
push.add_argument('--force', action='store_true', help='Force push')
# pull subcommand
pull = sub.add_parser('pull', help='Pull data from remote')
pull.add_argument('--depth', type=int, default=1, help='Clone depth')
args = parser.parse_args()
print(f'Command: {args.command}, Force: {getattr(args, "force", False)}')
JavaScript
// Node.js built-in process.argv is the raw array
const args = process.argv.slice(2);
console.log(args);
// Commander.js is the most popular CLI framework for Node.js
// npm install commander
import { Command } from 'commander';
const program = new Command();
program
.argument('<input>', 'Input file path')
.option('-o, --output <file>', 'Output file path', 'out.txt')
.option('-v, --verbose', 'Enable verbose logging')
.action((input, options) => {
console.log(`Input: ${input}, Output: ${options.output}, Verbose: ${options.verbose}`);
});
program.parse();
Java
// picocli is the modern standard for Java CLI
// 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 = "Input file path")
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);
}
}
Explanation
CLI frameworks take the raw argument array (sys.argv, process.argv, or String[] args) and turn it into typed
values. They generate help text, validate required arguments, and cast values like --count 5 to integers. Boolean
flags, positional arguments, variadic inputs, subcommands — all handled.
Python’s argparse ships with the standard library and covers most scripts. Click takes a decorator-based approach
that composes more cleanly. For JavaScript, commander is the go-to choice with its chainable API. picocli uses Java
annotations and plays well with GraalVM native-image compilation for fast-startup CLIs.
A pattern you’ll see often: pair CLI flags with a config file. Read --config settings.yaml first, then let
flags from the command line override specific values. See Parse YAML Files in Python and JavaScript
and Parse TOML Files in Python and Java for examples of reading config files that pair
well with CLI parsing.
If your CLI processes structured input, validate it against a schema before running. Check Validate JSON Schema in Python and JavaScript for schema validation patterns that work alongside CLI argument parsing.
Variants
| Technology | Library | Approach | Notes |
|---|---|---|---|
| Python | argparse | Standard library | Zero dependencies, auto-generated help |
| Python | Click | Decorators | Composable, supports progress bars and prompts |
| Python | typer | Type hints | Built on Click, uses Python 3.6+ annotations |
| JavaScript | commander | Fluent API | Most popular, supports subcommands |
| JavaScript | yargs | Middleware chain | Highly extensible, good for complex CLIs |
| Java | picocli | Annotations | Auto-completion scripts, native-image support |
| Java | Apache Commons CLI | Builder pattern | Older but widely used in enterprise |
Best Practices
Use standard libraries first (argparse, process.argv) for simple scripts to avoid dependency bloat. Add -h and
--help flags to every CLI, because the frameworks generate them automatically. Validate required arguments early and
show friendly errors, not raw stack traces.
Support --version so users and CI/CD pipelines can pin tooling versions. Exit with 0 for success and a non-zero code
on failure, so calling shell scripts can spot problems.
Common Mistakes
Parsing process.argv manually instead of using a framework leads to brittle, unmaintainable code. When required
arguments go missing, users should see help text, not a stack trace.
Mutating global state in CLI handlers makes both testing and composition harder. Ignoring exit codes means CI/CD
pipelines can’t detect failures when the CLI always exits with 0. Over-engineering subcommands is also common: one
script with flags is usually simpler than a multi-level CLI.
When Not to Use
A script that only needs a single fixed input doesn’t need a CLI — use a function or an environment variable. If a tool is only run by other scripts, a configuration file or environment variables may be cleaner than flags. For hot paths where parsing overhead matters, prefer pre-parsed or compiled options.
Summary
- Use
argparsefor Python scripts with zero dependencies; reach forClickwhen you need composition and decorators. commanderis the standard choice for Node.js CLIs, withyargsas a more extensible alternative.picocliis the modern Java standard, with annotation-based config and GraalVM native-image support.- Always provide
--helpand--versionflags; frameworks generate them automatically. - Exit
0on success, non-zero on failure — that’s how CI/CD pipelines detect problems. - Combine CLI flags with config files (YAML, TOML) for tools that need persistent or complex configuration.
- Full companion code with tests: stack-practices-resources.
See Also
- Python argparse documentation — Official docs for the standard library parser, with examples for every argument type.
- Commander.js documentation — Node.js CLI framework with fluent API and subcommand support.
- picocli documentation — Java CLI framework with annotations, autocompletion, and native-image support.
- Click documentation — Python CLI framework with decorators and composable commands.
- 12 Factor CLI Apps — Principles for building maintainable CLI tools that work well in containers and CI.
Frequently Asked Questions
How do I handle environment variables alongside CLI arguments?
Use libraries that natively support env var fallbacks. Click exposes envvar=, and picocli exposes
defaultValue = "${ENV_VAR}". Environment variables are ideal for secrets and deployment-specific values that shouldn't
appear in shell history.
What is the best way to test CLI applications?
Invoke the CLI entry point as a function rather than spawning subprocesses. Python Click has runner.invoke(),
picocli has CommandLine.execute() in-process, and commander can be tested by calling .parse() with a mock argv
array. That runs much faster than shell-based testing.
How do I build a CLI with subcommands?
Every major framework supports subcommands. Use add_subparsers() in argparse, .command() in commander, and
@Command on nested classes in picocli. Put shared options in a parent class or mixin to avoid duplication.
How do I validate argument types beyond what the framework provides?
Each framework lets you plug in custom validation. argparse accepts any callable as type=. Click gives you
click.IntRange and custom ParamType subclasses. commander takes a parse function as the third argument to
.option(). picocli uses @Option(type=...) with built-in or custom ITypeConverter implementations. Validate
early and fail with a clear error message.
What exit codes should I use?
Follow the POSIX convention: 0 for success, 1 for general errors, 2 for argument parsing errors. argparse
exits with 2 on parse failures by default. picocli returns the value from call() as the exit code. commander
sets process.exitCode rather than calling process.exit() directly, so async handlers can finish first.
How do I add autocomplete to my CLI?
argparse doesn't support autocomplete natively, but argcomplete adds it with a single decorator. Click produces
completion scripts through click.completion. commander handles completion via the commander-completion plugin.
picocli includes picocli.AutoComplete for generating completion scripts.
Related Resources
Parse YAML Files
How to parse YAML configuration files in Python, Java, and JavaScript.
RecipeParse and Write TOML Files: Python, Java & JS Examples
How to parse and write TOML configuration files in Python, Java, and JavaScript.
RecipeValidate JSON Schema
How to validate JSON data against schemas in Python, Java, and JavaScript.
RecipeParse CSV Files
How to parse CSV files in Python, Java, and JavaScript with practical code examples.
RecipeParse JSON
How to parse JSON strings into native data structures across multiple programming languages.
RecipeParse Server Log Files with Python, Java and JavaScript
Parse and analyze server log files with Python, Java, and JavaScript. Covers regex, structured logging, real-time tailing, and security.