Parsing the Sentence
When you type a command into a terminal, the shell interprets it as a structured sentence with a grammar. To build a good tool, you need to understand that grammar — both so you can design your own tool's interface correctly, and so you can recognize when you're violating conventions that users expect.
Let's dissect a typical command: mytool deploy my-app --env=production -v
1. The Program (mytool): The executable entry point. This is the binary or script the shell finds by searching directories listed in your $PATH environment variable.
2. Subcommands (deploy): Modern CLIs group related functionality under subcommands. Think git commit, git push, docker run, kubectl get. Subcommands let a single binary expose many distinct operations without becoming a monolithic mess of flags. When your tool grows beyond two or three distinct operations, subcommands are the right design choice.
3. Positional Arguments (my-app): Essential inputs required by the command. Their meaning is defined entirely by their position. If you swap them, the command changes meaning — mv source.txt dest.txt is very different from mv dest.txt source.txt. Positional arguments are for inputs that are always required and whose role is unambiguous from context.
4. Flags/Options (--env=production, -v): Optional modifiers that change how the command runs. There are two syntactic forms:
* Long Flags: Start with two dashes (--verbose, --environment=production). They are self-documenting and preferred in scripts because a reader can understand them without memorizing the tool's interface.
* Short Flags: Start with one dash (-v, -e production). They are faster to type interactively. Many short flags can be combined: ls -lah is identical to ls -l -a -h.
Don't Parse Arguments Yourself
Novice scriptwriters try to parse arguments by manually inspecting the raw input array — sys.argv in Python, or $1, $2 in Bash. This approach is brittle: it breaks when flags appear in a different order, it can't handle combined short flags, it produces no help text, and it requires you to write dozens of lines of error-handling code that argument parsing libraries give you for free.
Always use an argument parsing library. Every language has mature options:
| Language | Libraries |
|---|---|
| Python | argparse (stdlib), click, typer |
| Go | cobra, flag (stdlib) |
| Node.js | commander, yargs |
| Rust | clap |
| Bash | getopts (builtin) |
These libraries handle the messy parsing logic and, critically, generate help documentation automatically from your argument definitions. You define the interface once; the library handles both parsing and documentation.