Handling Command-Line Input

Command-line arguments are the simplest interface between a user and your Python program. Everything after the script name lives in sys.argv as a list of strings. A tiny script can parse those strings by hand; anything larger should use argparse, the standard library's declarative parser. It generates the --help text, coerces types, validates choices, and fails with a useful message when the input is wrong.

An argparse parser is typically defined in a few lines. You create an ArgumentParser, add positional arguments (parser.add_argument("path")), add optional flags (parser.add_argument("--verbose", action="store_true")), and call parser.parse_args(). The result is a Namespace with the named attributes.

Every user-facing option should follow two conventions: use -s / --long names, and accept --help for free. argparse wires both up automatically. For subcommands (git add, git commit style) use subparsers; for reading from stdin as a fallback, combine argparse with sys.stdin.

When the user provides interactive input at runtime rather than on the command line, input(prompt) is the built-in primitive. It returns a string; convert with int() or float() and handle the inevitable ValueError. For passwords, getpass.getpass() reads without echoing. For long-running tools, cmd.Cmd or click/typer (third-party) give you richer REPL-style UIs.

argparse basics

add_argument("name", type=int, default=0, help="...") covers typical cases. action="store_true" for boolean flags; choices=[...] for enumerations; required=True on optional arguments; nargs="+" for one-or-more values. Every argument you add appears in the auto-generated help.

For complex tools, group arguments with parser.add_argument_group and register sub-commands with parser.add_subparsers(). Each sub-parser has its own flags, which keeps a busy tool understandable.

Interactive input vs arguments

Arguments are best when the script is automated or embedded in a pipeline. Interactive input() is best when a human runs the script by hand once in a while and wants to be prompted. Many good tools accept both: fall back to input() only when the argument isn't provided.

Always wrap int(input(...)) in a loop that catches ValueError and re-prompts. Trust no input; validate before use.

Command-line tools and helpers.

ToolPurpose
argparse.ArgumentParser
class
Declarative CLI parser.
parser.add_argument
method
Add a positional or optional argument.
parser.add_subparsers
method
Register subcommands.
sys.argv
list
Raw arguments (program name first).
input(prompt)
built-in
Interactive single-line input.
getpass.getpass
function
Secret input without echo.
sys.exit(code)
function
Exit with a specific status code.
sys.stdin
stream
Read piped input line by line.

Handling Command-Line Input code example

The script builds a mini argparse CLI and runs it programmatically so the output is stable for teaching.

# Lesson: Handling Command-Line Input
import argparse
import sys


def build_parser() -> argparse.ArgumentParser:
    p = argparse.ArgumentParser(
        prog="greet",
        description="Greet a user with an optional language.",
    )
    p.add_argument("name", help="name of the person to greet")
    p.add_argument(
        "--lang", default="en", choices=["en", "fr", "es"],
        help="language for the greeting (default: en)",
    )
    p.add_argument(
        "-n", "--times", type=int, default=1,
        help="how many times to repeat (default: 1)",
    )
    p.add_argument(
        "--shout", action="store_true",
        help="print the greeting in uppercase",
    )
    return p


def greet(name: str, lang: str, times: int, shout: bool) -> list[str]:
    templates = {"en": "Hello, {}", "fr": "Bonjour, {}", "es": "Hola, {}"}
    line = templates[lang].format(name)
    if shout:
        line = line.upper()
    return [line] * times


parser = build_parser()

# Run a few example argument lists as if the user typed them
for argv in (
    ["Ana"],
    ["Ben", "--lang", "fr"],
    ["Cai", "-n", "3", "--shout"],
):
    args = parser.parse_args(argv)
    print(f"$ greet {' '.join(argv)}")
    for line in greet(args.name, args.lang, args.times, args.shout):
        print("  ", line)

# Show auto-generated help (also what --help would print)
print("\n--- help text ---")
parser.print_help()

Notice how little ceremony is needed:

1) One positional argument (name) and three options cover a small CLI.
2) `choices=[...]` validates input; bad values produce a clear error automatically.
3) `parser.parse_args(argv)` accepts a list, which is how you test without sys.argv.
4) `print_help()` is what the user sees with `--help`; no manual docs required.

Write a tiny file-stats CLI.

# stats.py (pseudo CLI)
import argparse, sys
from pathlib import Path

def main(argv: list[str] | None = None) -> int:
    p = argparse.ArgumentParser()
    p.add_argument("path", type=Path)
    p.add_argument("-l", "--lines", action="store_true")
    args = p.parse_args(argv)
    text = args.path.read_text(encoding="utf-8")
    if args.lines:
        print(len(text.splitlines()))
    else:
        print(len(text))
    return 0

# Run programmatically
from tempfile import NamedTemporaryFile
with NamedTemporaryFile("w", delete=False, encoding="utf-8") as f:
    f.write("a\nb\nc\n")
    name = f.name
main([name, "--lines"])

Fast checks of the parser.

import argparse
p = argparse.ArgumentParser()
p.add_argument("name")
p.add_argument("--n", type=int, default=1)
assert p.parse_args(["ana", "--n", "3"]).n == 3
try:
    p.parse_args([])
except SystemExit:
    pass

Running the demo prints:

$ greet Ana
   Hello, Ana
$ greet Ben --lang fr
   Bonjour, Ben
$ greet Cai -n 3 --shout
   HELLO, CAI
   HELLO, CAI
   HELLO, CAI

--- help text ---
usage: greet [-h] [--lang {en,fr,es}] [-n TIMES] [--shout] name
...