Building a CLI script
Structure a Python script with a main() function, argparse for flags, and the if __name__ == '__main__' guard.
- Structure a Python script with a main() function
- Explain why if __name__ == '__main__' matters
- Add a --dry-run flag with argparse
- Print clear progress output during a bulk operation
The difference between a script and a tool is structure. A script is a file of
statements you run once; a tool is something you run repeatedly, with options, by
callers who depend on its exit code to know if it succeeded.
people other than you. The gap is smaller than it sounds — it's mostly a main()
function and argparse.
The main() function
Wrap all your logic in a main() function and call it at the bottom:
def main():
print("doing the work...")
if __name__ == "__main__":
main()This is the canonical Python script structure. The logic lives in main(), which
makes it testable (you can import and call main() from a test file) and readable
(the function name documents the intent).
Why if name == 'main' matters
Every Python file has a built-in __name__ variable. When you run a file
directly (python my_script.py), Python sets __name__ to "__main__". When
you import it (import my_script), Python sets __name__ to the module name
instead.
The guard if __name__ == "__main__": means "only run this block if this file was
executed directly, not imported." Without it, importing your module as a library
would immediately execute your script logic — renaming files, writing output,
making network requests — which is almost never what you want.
Adding flags with argparse
argparse is the standard library module for parsing command-line arguments. A
--dry-run flag is the minimum useful addition to any script that modifies data:
import argparse
def parse_args():
parser = argparse.ArgumentParser(description="Process CSV files.")
parser.add_argument(
"--dry-run",
action="store_true",
help="Print what would happen without doing it.",
)
parser.add_argument(
"input_dir",
help="Directory containing input CSV files.",
)
return parser.parse_args()
def main():
args = parse_args()
print(f"Input dir : {args.input_dir}")
print(f"Dry run : {args.dry_run}")
# rest of the logic here
if __name__ == "__main__":
main()action="store_true" means the flag is a boolean: present means True, absent
means False. argparse also automatically generates a --help message from the
help= strings — run any argparse script with --help to see it.
Keep parse_args() separate from main(). It makes testing easier: you can call
main() with pre-built args without going through the argument parser.
A complete skeleton
Here is the full pattern put together. Try running it, then modify the logic:
The runner simulates the argparse output with a plain class so you can focus on
the structure without the full CLI machinery. In a real script you replace Args()
with parse_args().
Where to go next
Next: lab — automate a task — put everything together by writing a complete script that reads a CSV, filters and counts records, and prints a summary report.
What makes a good script?
Four properties separate scripts you trust from ones that cause surprises — idempotency, dry-run mode, clear output, and graceful errors.
Lab: Automate a task
Write a complete script that reads a CSV of tasks, filters by status, counts by category, and prints a summary report.