Even tiny scripts become reusable once they take file names or options from the command line.
When you run python greet.py Ann --loud, Python stores the command line in sys.argv, a list of strings:
sys.argv[0]is the script name as given ('greet.py'), or'-c'when runningpython -c, or the full path of the module withpython -m.sys.argv[1:]are the arguments, always as strings, split by the shell: quoting"New York"keeps the space inside one argument.
Everything arrives as text, so convert explicitly: int(sys.argv[2]). Check the length before indexing, or you get an IndexError when the user forgets an argument; print a usage message to stderr and exit with a non-zero code instead.
For anything beyond one or two positional arguments, such as options, defaults, --help and type checking, use argparse, which reads sys.argv[1:] for you. Writing your own option parser over sys.argv is a common source of bugs.
A useful pattern is to pass the argument list into main(argv) instead of reading sys.argv inside your logic. Tests can then call main(["a.txt", "-n", "3"]) directly without touching global state.
sys.orig_argv (3.10+) holds the full original command line including interpreter options like -X dev.
Example
import subprocess
import sys
script = '''
import sys
def main(argv):
if len(argv) < 1:
print("usage: greet NAME [TIMES]", file=sys.stderr)
return 2
name = argv[0]
times = int(argv[1]) if len(argv) > 1 else 1
print(" ".join([f"hi {name}"] * times))
return 0
sys.exit(main(sys.argv[1:]))
'''
open("greet.py", "w").write(script)
run = lambda *a: subprocess.run([sys.executable, "greet.py", *a],
capture_output=True, text=True)
print(run("Ann", "2").stdout.strip()) # hi Ann hi Ann
print(run("New York").stdout.strip()) # hi New York
print(run().returncode) # 2Remember
sys.argv[0]is the script;sys.argv[1:]are the arguments, all strings- Validate the count and convert types yourself, or use argparse
- Pass
sys.argv[1:]intomain(argv)so the logic is testable
Watch out for
- Indexing
sys.argv[1]without checking the length; a missing argument crashes with IndexError instead of a usage message.