Introduction
Taking input from the command line is a fundamental skill for any Python developer who writes scripts, utilities, or small applications. Whether you are building a data‑processing tool, a configuration‑driven program, or a simple CLI utility, the ability to read arguments supplied by the user at runtime makes your code more flexible and reusable. argvand theargparse module—and provides practical examples, best‑practice tips, and answers to frequently asked questions. Even so, this article explains *how* to capture command‑line input in Python, covers the two primary methods—sys. By the end, you will be able to design reliable command‑line interfaces that handle arguments, validate data, and offer helpful help messages without sacrificing readability It's one of those things that adds up. Surprisingly effective..
Using sys.argv to Capture Arguments
Understanding sys.argv
The sys module provides a list called argv that contains every token passed to the Python interpreter when the script is executed. Also, the first element (argv[0]) is always the script’s filename, and the subsequent elements represent the arguments supplied by the user. This approach is straightforward and works for any Python version, making it ideal for quick scripts It's one of those things that adds up..
import sys
# Example: python myscript.py hello world 42
print(sys.argv) # Output: ['myscript.py', 'hello', 'world', '42']
Basic Example
Here is a minimal script that greets the user and optionally prints a number:
import sys
def main():
# Ensure at least one argument (the name) is provided
if len(sys.argv) < 2:
print("Usage: python greet.py [number]")
sys.
name = sys.Now, argv[1] # First user‑supplied argument
number = int(sys. argv[2]) if len(sys.
print(f"Hello, {name}!")
if number:
print(f"The optional number you gave is {number}")
if __name__ == "__main__":
main()
Key points
len(sys.argv)tells you how many arguments were supplied.- Type conversion (
int(),float(), etc.) must be performed manually becausesys.argvalways returns strings. - Error handling (checking list length) prevents crashes when required arguments are missing.
Handling Optional Arguments
Optional arguments can be represented by flags such as --verbose or -v. With sys.argv, you simply check for the presence of a specific string:
import sys
def main():
verbose = "--verbose" in sys.Here's the thing — argv
# Strip the script name and any known flags
args = [arg for arg in sys. Day to day, argv or "-v" in sys. argv[1:] if not arg.
if not args:
print("Please provide a filename.")
sys.exit(1)
filename = args[0]
print(f"Processing {filename} in {'verbose' if verbose else 'quiet'} mode.")
if __name__ == "__main__":
main()
This snippet demonstrates how to detect flags and filter out non‑argument tokens, keeping the core logic clean.
Leveraging argparse for Professional‑Grade CLI
While sys.argv works for simple cases, the argparse module offers a higher‑level, more maintainable way to define and parse command‑line interfaces. It automatically generates help text, handles type conversion, supports optional and required arguments, and provides built‑in error messages.
Setting Up an ArgumentParser
import argparse
def build_parser():
parser = argparse.",
type=int,
default=0,
help="An optional integer (default: 0)."
)
parser.Which means add_argument(
"number",
nargs="? "
)
parser.add_argument(
"name",
help="The name of the person to greet.So argumentParser(
description="A sample script that demonstrates argparse usage. "
)
parser.add_argument(
"-v", "--verbose",
action="store_true",
help="Enable verbose output.
def main():
parser = build_parser()
args = parser.parse_args() # <-- this reads sys.argv automatically
if args.verbose:
print(f"Verbose mode is ON")
print(f"Greeting: {args.name}")
print(f"Number: {args.
if __name__ == "__main__":
main()
Why argparse is preferable
- Automatic help generation: Running
python script.py -hprints a nicely formatted usage message. - Type validation:
type=intensures the argument is an integer; otherwise, a clear error is shown. - Default values: The
defaultparameter eliminates the need for manual checks. - Action flags:
store_truecreates a boolean flag (--verbose) without extra parsing logic.
Sub‑commands and Nested Arguments
For more complex tools (e.g., version control systems), argparse supports sub‑commands via add_subparsers That's the part that actually makes a difference..
parser = argparse.ArgumentParser(prog="mytool")
subparsers = parser.add_subparsers(dest="command", required=True)
# subcommand: init
init_parser = subparsers.add_parser("init", help="Initialize a project")
init_parser.add_argument("path", help="Directory to initialize")
# subcommand: run
run_parser = subparsers.add_parser("run", help="Run the tool")
run_parser.add_argument("-n", "--number", type=int, required=True)
# subcommand: show
show_parser = subparsers.add_parser("show", help="Display information")
show_parser.add_argument("-f
Continuing the example, we’ll finish the **show** sub‑command and then tie everything together in a functional `main()`:
```python
# subcommand: show
show_parser = subparsers.add_parser("show", help="Display information")
show_parser.add_argument("-f", "--format", choices=["text", "json", "yaml"],
default="text", help="Output format (default: text)")
show_parser.add_argument("-l", "--list", action="store_true",
help="List all available items instead of a single one")
Now that the parsers are defined, we need a routine that dispatches to the appropriate logic based on the selected sub‑command:
def handle_init(args):
"""Initialize a new project at the given path."""
print(f"Initializing project in '{args.path}'...")
# …create .gitignore, config files, etc.
def handle_run(args):
"""Execute the tool with the supplied number."""
print(f"Running with number = {args.number}")
# …perform the core operation
def handle_show(args):
"""Show data using the requested format and options.list:
print(f"Listing items in {fmt} mode")
# …fetch and display a list
else:
print(f"Showing a single item in {fmt} mode")
# …fetch and display a single item
def main():
parser = argparse.format
if args."""
fmt = args.ArgumentParser(prog="mytool")
subparsers = parser.
# Re‑create the sub‑command parsers (or import them from a separate module)
init_parser = subparsers.add_parser("init", help="Initialize a project")
init_parser.add_argument("path", help="Directory to initialize")
run_parser = subparsers.add_parser("run", help="Run the tool")
run_parser.add_argument("-n", "--number", type=int, required=True)
show_parser = subparsers.Practically speaking, add_parser("show", help="Display information")
show_parser. add_argument("-f", "--format", choices=["text", "json", "yaml"],
default="text", help="Output format (default: text)")
show_parser.
args = parser.parse_args()
# Dispatch to the handler that matches the sub‑command
dispatch = {
"init": handle_init,
"run": handle_run,
"show": handle_show,
}
dispatch
if __name__ == "__main__":
main()
Advanced Argparse Techniques
While the basic pattern above covers most everyday needs, argparse also provides several features that make CLI tools more solid and user‑friendly:
| Feature | What it does | Typical use‑case |
|---|---|---|
choices= |
Restricts an argument to a predefined set of values. | Validating enumerated options (--mode {dev,prod,staging}). Also, |
mutually_exclusive_group() |
Guarantees that only one argument from a group is supplied. | --input vs --archive – you can provide either, but not both. Which means |
type=custom_function |
Applies a user‑defined conversion before validation. | Parsing dates (type=lambda s: datetime.So strptime(s, "%Y-%m-%d")). |
const & nargs='?Worth adding: ' |
Supplies a default value when the optional argument is omitted but present (-x). Day to day, |
-f txt where -f alone means “use txt”. |
default=argparse.SUPPRESS |
Prevents the attribute from appearing in args when the argument isn’t supplied. |
Keeping the namespace clean for optional configuration. Consider this: |
conflict_handler='resolve' |
Resolves duplicate argument names by overwriting. | Loading plugins that may add the same flag. Plus, |
Custom action classes |
Extend how arguments are stored or processed. | StoreUniqueAppend to collect a list without duplicates. |
Integrating with Logging and Error Handling
A professional CLI often combines argparse with Python’s logging module. After parsing, you can configure the log level based on a --verbose flag (or -vv for debug) and emit structured messages:
import logging
def setup_logging(verbose=False):
level =