Last Updated: September 29, 2021
·
69.29K
· austinkeeley

Give your Python program a shell with the cmd module

There's a Python module that I recently became obsessed with called cmd (Python docs). You can use it to give your Python programs a shell interface. For some reason, I never knew about this module for a long time, but once I learned about it, I began using it a lot to build some awesome utilities.

Python is one of my favorite languages because it lets you write lots of cool utilities and scripts for getting stuff done. For most small scripts, I'll just pass input as command line arguments but sometimes I'll want something a little more interactive and to give me instant feedback and to maintain a state. That's where the cmd module really makes it easy to write powerful Python scripts that appeal to my "power user" side.

If you were to do this on your own, you would probably write something like this:

while in_command_loop:
    raw_input = input("Prompt>")
    if input == 'command_1':
        command_1()
    elif input == 'command_2':
        command_2()
    # more commands here...
    else:
        print "Error"

So that's not too terrible. We loop and get commands as input and run functions. One problem is that we'll spend some time updating this loop when we want to add a new command and that's just another if statement in this thing. Not elegant. We can do better. We're Python developers.

First thing to know about cmd.Cmd: you subclass it and customize the subclass to be your command prompt. The methods of this subclass that begin with do_ are now your prompt commands. Here's a trivial example:

from cmd import Cmd

class MyPrompt(Cmd):

    def do_hello(self, args):
        """Says hello. If you provide a name, it will greet you with it."""
        if len(args) == 0:
            name = 'stranger'
        else:
            name = args
        print "Hello, %s" % name

    def do_quit(self, args):
        """Quits the program."""
        print "Quitting."
        raise SystemExit


if __name__ == '__main__':
    prompt = MyPrompt()
    prompt.prompt = '> '
    prompt.cmdloop('Starting prompt...')

Running that gives the following:

Starting prompt...
> help

Documented commands (type help <topic>):
========================================
hello  help  quit

> help hello
Says hello. If you provide a name, it will greet you with it.
> hello austin
Hello, austin
> 

Cool things to notice about this:
- Self documenting commands: Your prompt contains an implicit help command which will show you some help text for the command. Where does that come from? It's your method doc string!
- Command history: You you press the up and down keys and scroll through a list of commands. Some OS shells already do this for you, but it's still a nice feature.
- Organization: All of your commands are just methods and they all have a command method signature. I like how comfortable this feels compared to a massive block of if... elif... else statements.
- Lots of features: I didn't touch any of the really neat things, like pre-command and post-command code being executed.

So in conclusion, the cmd module lets you build a command line shell for your Python program without a lot of extra effort. You get to keep using the command line (like all real geeks use) and get some interactivity.

6 Responses
Add your response

Thanks for sharing this tip. I didn't know this module either. I've been looking for that for a while.

over 1 year ago ·

Very informative article. I was searching for such example since long time.
Thank you very much.

over 1 year ago ·

As someone who has written their own interpreter a few times in other lanuguages, this is a great example. I was off an running quickly after reading this.

over 1 year ago ·

How can I pull this interactive shell onto a web page using html?

over 1 year ago ·

Or you could just use exec(command)

over 1 year ago ·

Thanks for the article. It's amazing how close my old code was to your pre-cmd version. I became a true believer in minutes and re-did my command line module. Thanks again, Douglas

over 1 year ago ·