Introduction
Command line arguments let users pass options and data to a program at launch. Every language provides access to raw arguments as a string array, but most also have a dedicated parsing library that handles flags, options, help text, and validation. Python uses argparse, Node.js uses commander or yargs, Go uses flag, and Rust uses clap. Using a parsing library is almost always better than manually splitting argv.
Python (argparse)
1import argparse
2
3parser = argparse.ArgumentParser(description="Process some data")
4parser.add_argument("input", help="Input file path")
5parser.add_argument("-o", "--output", default="result.txt", help="Output file path")
6parser.add_argument("-n", "--count", type=int, default=10, help="Number of items")
7parser.add_argument("-v", "--verbose", action="store_true", help="Enable verbose output")
8
9args = parser.parse_args()
10print(f"Input: {args.input}")
11print(f"Output: {args.output}")
12print(f"Count: {args.count}")
13print(f"Verbose: {args.verbose}")
1$ python script.py data.csv -o output.txt -n 50 -v
2Input: data.csv
3Output: output.txt
4Count: 50
5Verbose: True
6
7$ python script.py --help
8usage: script.py [-h] [-o OUTPUT] [-n COUNT] [-v] input
For simple scripts, sys.argv gives raw access:
import sys
print(sys.argv) # ['script.py', 'arg1', 'arg2']
print(sys.argv[1]) # 'arg1'
Node.js (process.argv and commander)
Raw access:
1// process.argv includes node path and script path
2console.log(process.argv);
3// ['/usr/bin/node', '/path/to/script.js', 'arg1', 'arg2']
4
5const args = process.argv.slice(2); // User arguments only
6console.log(args); // ['arg1', 'arg2']
Using commander:
1import { program } from 'commander';
2
3program
4 .argument('<input>', 'input file path')
5 .option('-o, --output <path>', 'output file', 'result.txt')
6 .option('-n, --count <number>', 'number of items', parseInt, 10)
7 .option('-v, --verbose', 'enable verbose output')
8 .parse();
9
10const options = program.opts();
11const input = program.args[0];
12console.log(`Input: ${input}`);
13console.log(`Output: ${options.output}`);
14console.log(`Count: ${options.count}`);
$ node script.js data.csv -o output.txt -n 50 -v
Go (flag package)
1package main
2
3import (
4 "flag"
5 "fmt"
6)
7
8func main() {
9 output := flag.String("output", "result.txt", "Output file path")
10 count := flag.Int("count", 10, "Number of items")
11 verbose := flag.Bool("verbose", false, "Enable verbose output")
12 flag.Parse()
13
14 // Positional arguments (after flags)
15 args := flag.Args()
16 if len(args) < 1 {
17 fmt.Println("Usage: program [flags] <input>")
18 return
19 }
20
21 fmt.Printf("Input: %s\n", args[0])
22 fmt.Printf("Output: %s\n", *output)
23 fmt.Printf("Count: %d\n", *count)
24 fmt.Printf("Verbose: %v\n", *verbose)
25}
$ go run main.go -output out.txt -count 50 -verbose data.csv
Rust (clap)
1use clap::Parser;
2
3#[derive(Parser)]
4#[command(about = "Process some data")]
5struct Args {
6 /// Input file path
7 input: String,
8
9 /// Output file path
10 #[arg(short, long, default_value = "result.txt")]
11 output: String,
12
13 /// Number of items
14 #[arg(short = 'n', long, default_value_t = 10)]
15 count: u32,
16
17 /// Enable verbose output
18 #[arg(short, long)]
19 verbose: bool,
20}
21
22fn main() {
23 let args = Args::parse();
24 println!("Input: {}", args.input);
25 println!("Output: {}", args.output);
26 println!("Count: {}", args.count);
27 println!("Verbose: {}", args.verbose);
28}
Java
1public class Main {
2 public static void main(String[] args) {
3 // Raw access
4 for (int i = 0; i < args.length; i++) {
5 System.out.println("arg[" + i + "] = " + args[i]);
6 }
7
8 // Simple manual parsing
9 String input = args[0];
10 String output = "result.txt";
11 boolean verbose = false;
12
13 for (int i = 1; i < args.length; i++) {
14 switch (args[i]) {
15 case "-o": output = args[++i]; break;
16 case "-v": verbose = true; break;
17 }
18 }
19 }
20}
For production Java applications, use Apache Commons CLI or picocli for proper argument parsing.
Bash
1#!/bin/bash
2
3# Using getopts for short options
4while getopts "o:n:v" opt; do
5 case $opt in
6 o) output="$OPTARG" ;;
7 n) count="$OPTARG" ;;
8 v) verbose=true ;;
9 \?) echo "Invalid option: -$OPTARG" >&2; exit 1 ;;
10 esac
11done
12shift $((OPTIND - 1))
13
14input="$1"
15echo "Input: $input"
16echo "Output: ${output:-result.txt}"
17echo "Count: ${count:-10}"
18echo "Verbose: ${verbose:-false}"
$ ./script.sh -o output.txt -n 50 -v data.csv
Common Pitfalls
Manually parsing argv in complex CLIs: Splitting arguments by hand misses edge cases like quoted strings, = signs in values (--output=file.txt), combined short flags (-vn 50), and -- to stop flag parsing. Use a proper parsing library.
Forgetting that argv[0] is the program name: In most languages, argv[0] is the script or executable path, not the first user argument. User arguments start at index 1 (or 2 in Node.js, which includes the node binary path).
Not providing help text: Users expect --help to show usage information. Argument parsing libraries generate this automatically, but raw argv parsing does not. Always include a help option.
Type coercion errors: Command line arguments are always strings. Forgetting to parse "50" to an integer causes type errors or unexpected string concatenation. Use the parsing library's type parameter (type=int in argparse, parseInt in commander).
Not validating required arguments: If a required positional argument is missing, the program should print usage instructions and exit with a non-zero code, not crash with an index-out-of-bounds error.
Summary
Use argparse (Python), commander (Node.js), flag (Go), or clap (Rust) instead of manual parsing
Raw argv access is fine for simple scripts with 1-2 arguments
Libraries handle flag parsing, type conversion, default values, help text, and validation automatically
argv[0] is the program name — user arguments start at index 1
Always provide --help output for command line tools
Validate required arguments and print clear error messages for missing or invalid input