Python: Command-Line Tools with argparse
Command-line programs are a natural fit for Linux and small computers. Python's standard-library argparse module handles options, help text, and input errors without needing an extra package.
A Small Command-Line Program
Save this as temperature.py:
#!/usr/bin/env python3
import argparse
def main():
parser = argparse.ArgumentParser(
description="Convert a Celsius temperature to Fahrenheit."
)
parser.add_argument(
"celsius",
type=float,
help="temperature in degrees Celsius",
)
args = parser.parse_args()
fahrenheit = args.celsius * 9 / 5 + 32
print(f"{fahrenheit:.1f} F")
if __name__ == "__main__":
main()
Running It
$ python3 temperature.py 18 64.4 F $ python3 temperature.py --help usage: temperature.py [-h] celsius Convert a Celsius temperature to Fahrenheit. ...
Good help text matters when a script is used months after it was written or by someone else on the team.
Optional Arguments
parser.add_argument(
"--output",
default="screen",
choices=["screen", "file"],
help="where to write the result",
)
Use required=True for options that must be supplied. Use choices when only a known set of values is valid.
Validate at the Boundary
Convert and validate command-line values as they enter the program. That keeps the rest of the code working with the right types instead of checking strings everywhere.
parser.add_argument(
"--retries",
type=int,
default=3,
help="number of attempts",
)
Make It Executable
$ chmod +x temperature.py $ ./temperature.py 18 64.4 F
The first line tells Unix which interpreter should run the file. Keeping scripts small, documented, and predictable makes them good building blocks for cron jobs and system services.
Standard Input and Output
Unix tools are easier to combine when they read from standard input and write normal results to standard output. Diagnostics belong on standard error.
import sys
for line in sys.stdin:
cleaned = line.strip()
if cleaned:
print(cleaned.upper())
$ printf 'one\ntwo\n' | python3 uppercase.py ONE TWO
Exit Codes
import sys
def main():
# do work here
return 0
if __name__ == "__main__":
sys.exit(main())
Returning an explicit status makes a tool dependable in cron jobs and larger shell scripts. Reserve non-zero values for failures or invalid input.
Logging Instead of Debug Prints
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
)
logging.info("starting report")
logging.warning("battery level is low")
Logging can later be redirected to a file or collected by systemd without rewriting every diagnostic message.
dispelled