Learn how to build Python scripts that accept command-line arguments, flags, and options — turning your scripts from hardcoded one-trick ponies into flexible, reusable tools.
- Why command-line arguments matter
sys.argv— the manual approach (and why it's painful)argparse— Python's built-in argument parser- Positional vs optional arguments
- Argument types, defaults, and choices
- Boolean flags (
--verbose,--dry-run) - Subcommands (like
git commit,git push) - Help text and documentation
- Fundamentals — functions, strings, control flow
- Error Handling — helpful for understanding how argparse reports errors
Right now, if you want to change the behavior of a script, you probably edit the code. That's fine for learning, but in the real world you want scripts that work like tools:
python3 backup.py /home/docs --compress --verbose
python3 resize_images.py photos/ --width 800 --format pngInstead of changing the code every time, you pass in what you want when you run it.
Python gives you sys.argv — a list of everything typed on the command line:
import sys
print(sys.argv) # ['script.py', 'arg1', 'arg2']This works but gets ugly fast. You have to manually check if arguments exist, convert types, handle errors, and write your own help text. For anything beyond one or two arguments, it's a headache.
argparse is a standard library module that handles all of that for you:
import argparse
parser = argparse.ArgumentParser(description="Greet someone")
parser.add_argument("name", help="Person to greet")
parser.add_argument("--shout", action="store_true", help="YELL the greeting")
args = parser.parse_args()
greeting = f"Hello, {args.name}!"
if args.shout:
greeting = greeting.upper()
print(greeting)Now you get:
- Automatic
--helpoutput - Type checking and error messages
- Clean access via
args.name,args.shout, etc.
Positional arguments are required and order matters:
parser.add_argument("filename") # Required, first thing after the script name
parser.add_argument("destination") # Required, second thingOptional arguments start with -- (or - for short form):
parser.add_argument("-v", "--verbose", action="store_true")
parser.add_argument("-o", "--output", default="result.txt")argparse can convert arguments to the right type and restrict allowed values:
parser.add_argument("count", type=int) # Must be an integer
parser.add_argument("--level", type=int, default=1) # Default to 1
parser.add_argument("--color", choices=["red", "green", "blue"]) # Only these values
parser.add_argument("--tags", nargs="+") # One or more values → listFor on/off switches, use store_true or store_false:
parser.add_argument("--verbose", action="store_true") # Default False, --verbose sets True
parser.add_argument("--no-color", action="store_true") # Default False, --no-color sets TrueFor tools that have multiple modes (like git commit vs git push):
subparsers = parser.add_subparsers(dest="command")
add_parser = subparsers.add_parser("add", help="Add an item")
add_parser.add_argument("item")
list_parser = subparsers.add_parser("list", help="List all items")
list_parser.add_argument("--all", action="store_true")A well-structured CLI script follows this pattern:
def main():
parser = argparse.ArgumentParser(description="What this tool does")
# ... add arguments ...
args = parser.parse_args()
# ... use args to do the work ...
if __name__ == "__main__":
main()This keeps your script both importable and runnable.
See example.py for runnable demonstrations of all these concepts.
Try the exercises in exercises.py — build your own CLI tools!
sys.argvworks but doesn't scale — useargparseinstead- Positional arguments are required; optional arguments start with
-- - argparse handles type conversion, defaults, choices, and error messages for you
store_trueis the go-to for boolean flags- Subcommands let you build multi-mode tools like
git - Always wrap your CLI logic in
main()withif __name__ == "__main__" - The auto-generated
--helpflag is one of argparse's best features — write good help text!