Python Command Line Arguments: sys.argv, argparse and getopt

Python command line arguments are the words you type after the script name, like python report.py sales.csv 2026. Python stores them in sys.argv, a list of strings. For named options, defaults and a free --help page, use the argparse module:

import sys
print(sys.argv[1:])   # python report.py sales.csv 2026 -> ['sales.csv', '2026']

Every script below was saved to a folder and started from a Windows Command Prompt on Python 3.12.5, so the output blocks and screenshots are exactly what the terminal printed.

In this tutorial, we’ll start with raw sys.argv, make it safe, then switch to argparse for options, flags, choices and subcommands. I’ll finish with getopt and a quick guide to picking one.

What are command-line arguments in Python?

A command-line argument is any value you add after the file name when you start a script. They let one script handle different files, dates or settings without editing the code each time.

Python puts them in sys.argv. Item 0 is the script name, and the rest are your arguments, split on spaces. I ran this three-line script with no arguments, three arguments, and a quoted city name:

import sys

print("Script:   ", sys.argv[0])
print("Arguments:", sys.argv[1:])
print("Count:    ", len(sys.argv) - 1)

Output:

C:\pyguides> python show_args.py
Script:    show_args.py
Arguments: []
Count:     0
C:\pyguides> python show_args.py report.csv 2026-10-20 --verbose
Script:    show_args.py
Arguments: ['report.csv', '2026-10-20', '--verbose']
Count:     3
C:\pyguides> python show_args.py "San Francisco" 3
Script:    show_args.py
Arguments: ['San Francisco', '3']
Count:     2
Command Prompt running a Python script three times and printing the sys.argv list each time
sys.argv[1:] is empty with no arguments; quotes keep "San Francisco" together as one item.

Two details matter here. '3' and '2026-10-20' arrive as strings, and --verbose is just another string in the list. sys.argv doesn’t parse or convert anything.

If you haven’t started a script from a terminal before, my guide on how to run a Python script covers opening Command Prompt and moving to the right folder.

Why does sys.argv[1] raise an IndexError?

Reading sys.argv[1] directly works until someone forgets the argument. Then the list only holds the script name, and Python raises an IndexError:

import sys

city = sys.argv[1]
print(f"Weather report for {city}")

Output:

C:\pyguides> python weather.py Boston
Weather report for Boston
C:\pyguides> python weather.py
Traceback (most recent call last):
  File "C:\pyguides\weather.py", line 3, in <module>
    city = sys.argv[1]
           ~~~~~~~~^^^
IndexError: list index out of range
Command Prompt showing a Python IndexError list index out of range when a sys.argv argument is missing
With Boston the script works; without it, sys.argv[1] doesn’t exist and the traceback says list index out of range.

A traceback like this confuses anyone who just wants to run your tool. The fix is to check len(sys.argv) first and print a usage line. More causes of this error are in fixing IndexError: list index out of range.

Validate and convert sys.argv values to numbers

This sales-tax calculator expects exactly two values. It checks the count, converts both strings with float(), and catches the ValueError you get when the text isn’t a number:

import sys

if len(sys.argv) != 3:
    print("Usage: python sales_tax.py PRICE RATE")
    sys.exit(1)

try:
    price = float(sys.argv[1])
    rate = float(sys.argv[2])
except ValueError:
    print("Error: PRICE and RATE must be numbers, e.g. 49.99 0.0825")
    sys.exit(1)

tax = price * rate
print(f"Price: ${price:.2f}")
print(f"Tax:   ${tax:.2f}")
print(f"Total: ${price + tax:.2f}")

Output:

C:\pyguides> python sales_tax.py 49.99 0.0825
Price: $49.99
Tax:   $4.12
Total: $54.11
C:\pyguides> python sales_tax.py 49.99
Usage: python sales_tax.py PRICE RATE
C:\pyguides> python sales_tax.py fifty 0.0825
Error: PRICE and RATE must be numbers, e.g. 49.99 0.0825
Command Prompt running a Python sales tax script with valid arguments, a missing argument and a non-numeric value
Two numbers give the total; one missing value prints the usage line, and the word fifty triggers the number error.

A $49.99 item at 8.25% tax comes to $54.11. The conversion step is the same one I cover in converting a string to a float in Python, and the try/except is plain Python exception handling.

sys.exit(1) stops the script and reports failure to the shell. A non-zero exit code is how batch files, schedulers and CI jobs know something went wrong.

This works, but you can see the pattern growing. Every new value needs its own count check, conversion and message. That’s the point where argparse saves real code.

Parse command-line arguments with argparse

argparse is in the standard library, so there’s nothing to install. You create a parser, describe each argument with add_argument(), and call parse_args(). Here’s a road-trip gas calculator:

import argparse

parser = argparse.ArgumentParser(description="Estimate the gas cost of a road trip.")
parser.add_argument("miles", type=float, help="one-way distance in miles")
parser.add_argument("--mpg", type=float, default=28.0, help="car fuel economy (default: 28)")
parser.add_argument("--price", type=float, default=3.45, help="gas price per gallon in USD (default: 3.45)")
parser.add_argument("-r", "--round-trip", action="store_true", help="double the distance")
args = parser.parse_args()

miles = args.miles * 2 if args.round_trip else args.miles
gallons = miles / args.mpg
print(f"Distance: {miles:,.0f} miles")
print(f"Gas used: {gallons:.1f} gallons")
print(f"Cost:     ${gallons * args.price:.2f}")

Output:

C:\pyguides> python trip_cost.py 380
Distance: 380 miles
Gas used: 13.6 gallons
Cost:     $46.82
C:\pyguides> python trip_cost.py 380 --mpg 34 --price 3.89 -r
Distance: 760 miles
Gas used: 22.4 gallons
Cost:     $86.95
Command Prompt running a Python argparse script with only the positional argument, then with mpg, price and round-trip options
The first run uses the default 28 mpg and $3.45 gas; the second sets 34 mpg, $3.89 gas and -r for a round trip.

miles is a positional argument: no dashes, required, and read by position. --mpg and --price are optional arguments with defaults, and type=float converts the text for you.

--round-trip uses action="store_true", so it’s a flag. It’s False unless you type it. -r is its short form, and argparse turns the dash in round-trip into args.round_trip.

Defaults on the command line behave like default parameters in a function, which I explain in Python functions with optional arguments.

The –help page and error messages argparse writes for you

You never wrote a help screen, yet -h prints one built from the help= strings. Missing or badly typed values are rejected before your code runs:

import argparse

parser = argparse.ArgumentParser(description="Estimate the gas cost of a road trip.")
parser.add_argument("miles", type=float, help="one-way distance in miles")
parser.add_argument("--mpg", type=float, default=28.0, help="car fuel economy (default: 28)")
parser.add_argument("--price", type=float, default=3.45, help="gas price per gallon in USD (default: 3.45)")
parser.add_argument("-r", "--round-trip", action="store_true", help="double the distance")
args = parser.parse_args()

miles = args.miles * 2 if args.round_trip else args.miles
gallons = miles / args.mpg
print(f"Distance: {miles:,.0f} miles")
print(f"Gas used: {gallons:.1f} gallons")
print(f"Cost:     ${gallons * args.price:.2f}")

Output:

C:\pyguides> python trip_cost.py -h
usage: trip_cost.py [-h] [--mpg MPG] [--price PRICE] [-r] miles

Estimate the gas cost of a road trip.

positional arguments:
  miles             one-way distance in miles

options:
  -h, --help        show this help message and exit
  --mpg MPG         car fuel economy (default: 28)
  --price PRICE     gas price per gallon in USD (default: 3.45)
  -r, --round-trip  double the distance
C:\pyguides> python trip_cost.py
usage: trip_cost.py [-h] [--mpg MPG] [--price PRICE] [-r] miles
trip_cost.py: error: the following arguments are required: miles
C:\pyguides> python trip_cost.py 380 --mpg high
usage: trip_cost.py [-h] [--mpg MPG] [--price PRICE] [-r] miles
trip_cost.py: error: argument --mpg: invalid float value: 'high'
Command Prompt showing argparse -h help output, a required argument error and an invalid float value error
-h lists every argument with its default; leaving out miles or typing high for mpg prints the usage line and a clear error.

On an error, argparse prints the usage line and exits with code 2. After -h it exits with 0. Both happen inside parse_args(), so the lines after it never run.

How do you add choices, lists and verbosity flags in argparse?

Most real tools need more than a number and a flag. add_argument() handles the common cases with keyword arguments. Passing a list to parse_args() lets you try them without a terminal:

import argparse

parser = argparse.ArgumentParser(prog="ship")
parser.add_argument("--carrier", choices=["ups", "fedex", "usps"], required=True)
parser.add_argument("--zip", dest="zip_codes", nargs="+", help="one or more ZIP codes")
parser.add_argument("-v", "--verbose", action="count", default=0)
parser.add_argument("--weight", type=float, default=1.0, metavar="LBS")

args = parser.parse_args(["--carrier", "ups", "--zip", "10001", "60601", "94105", "-vv"])
print(args)
print("ZIP codes:", args.zip_codes)
print("Verbosity level:", args.verbose)
print("As a dict:", vars(args))

Output:

Namespace(carrier='ups', zip_codes=['10001', '60601', '94105'], verbose=2, weight=1.0)
ZIP codes: ['10001', '60601', '94105']
Verbosity level: 2
As a dict: {'carrier': 'ups', 'zip_codes': ['10001', '60601', '94105'], 'verbose': 2, 'weight': 1.0}

Here’s what each setting did:

  • choices=[...] accepts only the listed values, and required=True makes an option mandatory.
  • nargs="+" collects one or more values into a list; use "*" for zero or more.
  • action="count" counts repeats, so -vv gives 2, which is handy for log levels.
  • dest renames the attribute, and metavar changes the placeholder shown in the help text.

vars(args) turns the parsed Namespace into a dictionary. That’s useful when you want to pass every option on with **, as shown in the difference between *args and **kwargs.

Mutually exclusive options

Sometimes two options can’t be used together, like --csv and --json. A mutually exclusive group enforces that. I set exit_on_error=False so the error comes back as an exception I can print:

import argparse

parser = argparse.ArgumentParser(prog="export", exit_on_error=False)
fmt = parser.add_mutually_exclusive_group()
fmt.add_argument("--csv", action="store_true")
fmt.add_argument("--json", action="store_true")

print(parser.parse_args(["--json"]))
try:
    parser.parse_args(["--csv", "--json"])
except argparse.ArgumentError as err:
    print("Error:", err)

Output:

Namespace(csv=False, json=True)
Error: argument --json: not allowed with argument --csv

In a normal script, leave exit_on_error at its default. The user then sees the usage line and the same message, and the script exits with code 2.

Build Git-style subcommands with add_subparsers()

Tools like git and pip use a verb first: pip install, pip list. In argparse, add_subparsers() gives each verb its own arguments. This small to-do tool has add and done:

import argparse

parser = argparse.ArgumentParser(prog="todo")
sub = parser.add_subparsers(dest="command", required=True)

add = sub.add_parser("add", help="add a task")
add.add_argument("task")
add.add_argument("--due", default="2026-10-31", help="due date (YYYY-MM-DD)")

done = sub.add_parser("done", help="mark a task number as done")
done.add_argument("number", type=int)

args = parser.parse_args()
if args.command == "add":
    print(f"Added: {args.task} (due {args.due})")
else:
    print(f"Task {args.number} marked as done")

Output:

C:\pyguides> python todo.py add "Pay rent" --due 2026-11-01
Added: Pay rent (due 2026-11-01)
C:\pyguides> python todo.py done 3
Task 3 marked as done
C:\pyguides> python todo.py -h
usage: todo [-h] {add,done} ...

positional arguments:
  {add,done}
    add       add a task
    done      mark a task number as done

options:
  -h, --help  show this help message and exit
C:\pyguides> python todo.py
usage: todo [-h] {add,done} ...
todo: error: the following arguments are required: command
Command Prompt running a Python argparse script with add and done subcommands, its help page, and a missing subcommand error
Each subcommand reads its own arguments; -h lists both verbs, and running with no command prints a required-argument error.

dest="command" stores which verb was used, so a simple if picks the right branch. required=True makes the script complain when no verb is given.

As a tool grows, move each branch into its own function. Writing the entry point as a main() function, with exit codes and tests, is covered in using the Python main function with arguments.

Keep unknown arguments with parse_known_args()

parse_args() rejects anything it doesn’t recognize. If your script passes some arguments on to another program, parse_known_args() returns the parsed values plus a list of everything else:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("--state", default="TX")
args, extra = parser.parse_known_args(["--state", "CA", "--dry-run", "orders.csv"])
print("Parsed:", args)
print("Left over:", extra)

Output:

Parsed: Namespace(state='CA')
Left over: ['--dry-run', 'orders.csv']

The leftover list is a normal list of strings, so you can forward it or check it yourself. To compare it against a list of allowed values, see checking if a list exists in another list.

Parse Unix-style options with the getopt module

getopt copies the C getopt() function. You give it short options as a string, where a colon means the option takes a value, and long options as a list. It returns option pairs and the remaining arguments:

import getopt

argv = ["-o", "summary.txt", "--verbose", "sales.csv", "returns.csv"]
opts, files = getopt.getopt(argv, "o:v", ["output=", "verbose"])
print("Options:", opts)
print("Files:  ", files)

try:
    getopt.getopt(["-x"], "o:v", ["output=", "verbose"])
except getopt.GetoptError as err:
    print("Error:", err)

Output:

Options: [('-o', 'summary.txt'), ('--verbose', '')]
Files:   ['sales.csv', 'returns.csv']
Error: option -x not recognized

It doesn’t convert types, write help, or check required values; you’d do all of that by hand. The Python docs suggest argparse if you want less code and better help and error messages, and I agree for new scripts.

You’ll still see getopt in older scripts and code ported from C or shell. If you only need to break one argument like "a b c" into words, splitting a string by whitespace is simpler.

Python command line arguments: build a script step by step

If this is your first command-line script, follow these steps:

  1. Make a folder such as C:\pyguides and create trip_cost.py in it.
  2. Add import argparse and create parser = argparse.ArgumentParser(description="...").
  3. Add one parser.add_argument() line per value, with type=, default= and help=.
  4. Call args = parser.parse_args() and read values as args.miles, args.mpg and so on.
  5. Open Command Prompt, run cd C:\pyguides, then python trip_cost.py -h to check the help text.
  6. Run it with real values, such as python trip_cost.py 380 --mpg 34 -r.
  7. Try a wrong value on purpose to confirm the error message makes sense.

Formatting the dollar amounts in the output uses f-strings, explained in formatting decimal places with f-strings.

sys.argv vs argparse vs getopt: which should you use?

ModuleBest forTypes, help and errors
sys.argvOne or two values in a quick scriptAll strings; you check count and convert
argparseMost scripts and toolsConverts types, writes -h, exits 2 on bad input
getoptMaintaining older or C-style scriptsSplits options only; no types or help
click / typerLarge CLIs, if you’re fine with a packageDecorators or type hints; installed with pip

My rule is simple. If a script takes one value and only I run it, sys.argv is fine. Once someone else runs it, or it needs an option, I reach for argparse.

If you like typer‘s type-hint style, the same annotations power dataclass default values and type hints, which can hold parsed settings neatly.

Want to keep going with input and functions? These pick up nicely from here:

Frequently asked questions

How do I get command-line arguments in Python?

Import sys and read sys.argv. sys.argv[0] is the script name and sys.argv[1:] holds the arguments as strings.

Why are my command-line arguments strings?

The shell passes plain text. Convert with int() or float(), or use type=int and type=float in argparse.

How do I pass an argument that contains spaces?

Wrap it in double quotes, like python show_args.py "San Francisco". The quotes keep it as one item in sys.argv.

How do I make a boolean flag with argparse?

Use action="store_true". The value is False by default and True when the flag is typed.

Is getopt deprecated in Python?

No, it’s still in the standard library. For new scripts, argparse is usually the better pick because it writes help and error messages for you.

Where is argparse documented?