Command-line tools are among the most practically useful things you can build as a developer. A well-built CLI tool can automate repetitive tasks, integrate with other tools, and be distributed to colleagues or the public. Here is how to build a good one in Python.
Why Python for CLIs
Python’s ecosystem for CLI development is excellent: rich library support, cross-platform compatibility, readable code that others can maintain, and fast enough for almost all CLI use cases. The alternative for performance-critical tools is Go (compiles to a single binary, very fast startup, excellent for distribution) or Rust (maximum performance, growing CLI ecosystem). Python’s weaknesses for CLIs: startup time (0.1–0.3s to import modules, noticeable if the tool runs thousands of times/day), no single-file binary distribution (requires Python installed, or use PyInstaller/Nuitka to build executables). For most developer tool use cases, Python is the right choice.
The Argument Parsing Layer
Do not use sys.argv directly. Use Click (the best library for most CLI tools), Typer (Click wrapper with type annotations, faster to write), or argparse (built-in, more verbose, fine for simple tools). Click example: a CLI that takes a file path and an optional verbose flag: `@click.command() / @click.argument(‘filepath’) / @click.option(‘–verbose’, ‘-v’, is_flag=True) / def process(filepath, verbose): …` Click handles: help text generation, input validation, subcommand grouping (git-style commands), and error messages. Typer uses Python type annotations instead of decorators, making the code slightly more readable and IDE-friendly. For a tool with 3+ subcommands: Click or Typer. For a simple single-purpose tool: argparse is sufficient.
Configuration and State
CLI tools that users run repeatedly need persistent configuration. The pattern: store configuration in platformdirs.user_config_dir() (cross-platform, returns the appropriate OS-specific path: ~/.config/toolname/ on Linux/Mac, %APPDATA%/toolname/ on Windows). Use TOML (tomllib in Python 3.11+, or tomli for earlier) for config files — more readable than JSON, supports comments. For API keys and secrets: use the keyring library (interfaces with macOS Keychain, Windows Credential Locker, and gnome-keyring on Linux) rather than storing secrets in plaintext config files. A `toolname config set api-key xxx` pattern lets users configure once without editing files.
Output and Error Handling
Good CLI output rules: use Rich (Python library) for formatted terminal output — tables, progress bars, coloured text, with automatic fallback for non-TTY output (when piped). Print errors to stderr, not stdout: `click.echo(msg, err=True)` or `print(msg, file=sys.stderr)`. This allows stdout to be piped while errors remain visible. Exit codes matter: exit 0 for success, 1 for general errors, 2 for usage errors (bad arguments). Use `sys.exit(1)` or Click’s exception handling rather than letting exceptions bubble up to the user as Python tracebacks. The professional touch: progress indicators for long operations (Rich’s Progress or tqdm), and a `–quiet` flag that suppresses informational output for scripting contexts.
![[Windows] 软件屏蔽器 v2.10.0.0](https://www.sunqi.org/wp-content/uploads/2021/12/feat-1388.jpg)


