80 — Tools: reading command-line arguments
So far every program asked for its input while running. A real tool reads
its instructions up front: nme r dice 6 should roll a six-sided die
without asking anything. Those words after the file name are command-line
arguments — Python puts them in the list sys.argv.
Steps
import sysgives yousys.argv: the program path plus every word typed after it. Savegreet.nme:
run it →# greet.nme — greet whoever is named on the command line. # Run: nme r greet Mina import sys name = sys.argv[1] show f"hello {name}"nme r greet Minahello Minasys.argv[0]is the program's path andsys.argv[1]is the first real argument — the same list a normalpython greet.py Minacommand makes.A missing argument must not crash confusingly.
len(sys.argv)counts the words; check it before reading one. Savedice.nme:
run it →# dice.nme — roll a die with the number of sides you ask for. # Run: nme r dice 6 use random latest import sys if len(sys.argv) < 2: show "usage: nme r dice <sides>" else: sides = int(sys.argv[1]) show random_number(1, sides)nme r dice 63int(sys.argv[1])converts the word"6"into the number 6, as in guide 02; without the check,nme r dicewould crash on an empty list. A "usage:" line that shows the right command is the friendly way to answer a missing argument.Arguments make a command-line tool: one program, several commands. A todo list stores its items in
todo.json(guide 14) and takesadd,done, andlistcommands. Savetodo.nme:
run it →# todo.nme — a todo tool that takes commands on the command line. # Run: nme r todo add "buy milk" # nme r todo list # nme r todo done 1 use file latest import sys todo_file = "todo.json" def load_todos(): try: return json_load(todo_file) except Exception: return [] def save_todos(todos): json_save(todo_file, todos) def show_todos(todos): for i, item in enumerate(todos): mark = "x" if item["done"] else " " show f"{i + 1}. [{mark}] {item['text']}" command = sys.argv[1] if len(sys.argv) > 1 else "list" todos = load_todos() if command == "add": text = sys.argv[2] if len(sys.argv) > 2 else "no text" todos.append({"text": text, "done": False}) save_todos(todos) show f"added: {text}" elif command == "done": number = int(sys.argv[2]) - 1 todos[number]["done"] = True save_todos(todos) show "marked done" else: show_todos(todos)load_todosreturns an empty list when the file does not exist yet (guide 68 explainstry/except), andenumeratenumbers the list starting at 1. Each item is a dict with atextand adoneflag — the shape thatjson_savewrites andjson_loadreads back.Run the tool as a chain of commands:
nme r todo add "buy milk" nme r todo add "learn argv" nme r todo done 1 nme r todoadded: buy milk added: learn argv marked done 1. [x] buy milk 2. [ ] learn argvThe quotes around
"buy milk"keep the two words together in one argument — without them,buyandmilkwould besys.argv[2]andsys.argv[3]. The filetodo.jsonis the tool's memory between runs.
Try it yourself
Add a clear command that empties the list, or a rm command that
removes one number (todos.pop(number)). Change the dice tool to accept
a list of sides and roll each (for side in sys.argv[1:]). Then make a
tool of your own: a unit converter (nme r convert 100 km to mi) is a
good first one.
What you learned
sys.argvholds the program path and every word typed after the file.len(sys.argv)guards against missing arguments; ausage:line tells the user the right command.int(sys.argv[1])turns an argument word into a number.- A command word (
add,done) plus data makes one program into many tools, like the mini bank's looped menu in guide 61 but given up front. - Quotes group several words into a single argument.