When writing an Command Line Interface for an application it could be nice to have an interactive shell with command completition and history. The cmd library of Python provides a framework for that.

We will build an application step-by-step. Scroll down to the end of the page to see a full example with all the bells and whistles.

Skeleton of the Interactive shell

The recommendation is to subclass the Cmd class, so that's what we do here, though as being a skeleton we don't do anything else with it.

examples/python/cli/skeleton.py

from cmd import Cmd

class MyPrompt(Cmd):
    pass

MyPrompt().cmdloop()

We create an instance object of the MyPrompt class and immediately call the cmdloop method. We could have used a temporary variable there if we wanted to be a bit more verbose like this:

p = MyPrompt()
p.cmdloop()

but the result is the same.

When we run this script it will display a default prompt:

(Cmd)

You can't do much with it. You can type in ? and press ENTER to get the following help:

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

help # (Cmd) Ctrl-C

You can ask for help on help by typing in:

help help

And it will print:

List available commands with "help" or detailed help with "help cmd".

If you would like to exit the application you need to press Ctlr-C and get a KeyboardInterrupt.

First commands

You can add commands to the system by implementing the corresponding do_* methods.

examples/python/cli/commands.py

from cmd import Cmd

class MyPrompt(Cmd):
   def do_exit(self, inp):
        print("Bye")
        return True

   def do_add(self, inp):
        print("Adding '{}'".format(inp))

MyPrompt().cmdloop()
print("after")

We implemented to commands: exit and add.

When we run the script it will show use the standard prompt:

(Cmd)

If at this point we press TAB twice we get the list of all the available command. In our case that is

add   exit  help

If we type in help we get the following output:

(Cmd) help

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

Undocumented commands:
======================
add  exit

If we type in help add as the help window suggests for the documented commands we get:

(Cmd) help add
*** No help on add

If we type in add followed by some text and press ENTER the system will run the do_add method and pass the text to the method. In our case we get:

(Cmd) add Hello World
Adding 'Hello World'

If we type in exit it will call the do_exit method, print "Bye" and exit the cmdloop. In our case it means it will go on and print the string "after".

(Cmd) exit
Bye
after

Help - documenting commands

As you could see above, the built-in help command had some documentation, but the two command we added did not have any. There are two ways to add documentation to a command. Either by adding a method with the help_* prefix or by adding docstring to the appropriate do_* method.

examples/python/cli/help.py

from cmd import Cmd

class MyPrompt(Cmd):
   def do_exit(self, inp):
        '''exit the application.'''
        print("Bye")
        return True

   def do_add(self, inp):
        print("Adding '{}'".format(inp))

   def help_add(self):
       print("Add a new entry to the system.")

MyPrompt().cmdloop()

Entering ? not all the commands are listed under the "Documented commands":

(Cmd) ?

Documented commands (type help <topic>):
========================================
add  exit  help

We can get help by typing in help and the name of the command:

(Cmd) help add
Add a new entry to the system.

(Cmd) help exit
exit the application.

We can still exit the application:

(Cmd) exit
Bye

Prompt and banner

The default prompt is (Cmd) but we can override it using the prompt attribute of the class.

In addition we can set a text to be the banner, that is the text shown when we launch the application, before the first prompt is shown. We only need to assign the text to the intro attribute.

examples/python/cli/prompt.py

from cmd import Cmd

class MyPrompt(Cmd):
    prompt = 'pb> '
    intro = "Welcome! Type ? to list commands"

    def do_exit(self, inp):
        '''exit the application.'''
        print("Bye")
        return True

MyPrompt().cmdloop()

If we run this application we'll see:

Welcome! Type ? to list commands
pb>

Default actions

Sometime you might want to be able to freely parse the input. For example if you'd like to implement short, one-letter alternatives of longer commands. If you implement a method called default that method will be called every time a command is entered that does not correspond to any of the do_* methods.

examples/python/cli/default.py

from cmd import Cmd

class MyPrompt(Cmd):

    def do_exit(self, inp):
        '''exit the application. Shorthand: x q.'''
        print("Bye")
        return True

    def default(self, inp):
        if inp == 'x' or inp == 'q':
            return self.do_exit(inp)

        print("Default: {}".format(inp))

MyPrompt().cmdloop()

In this example we catch stand-alone x and q characters and call the do_exit method for them.

Ctrl-d EOF

You might have noticed thet if you press Ctrl-d, the standard way to exit most command line application, our examples print *** Unknown syntax: EOF. That's because Ctrl-d send an EOF (End Of File) signal and by default Cmd does not know what to do with it.

The solution is to implement the do_EOF method that will be called when the user presses Ctl-d. As we already have a do_exit method, we can just assign that to the do_EOF and have both do the same. In order to provide help for the EOF, we can include a function called help_EOF that is assigned the help_exit function.

Full example

This examples includes all of the above techniques.

examples/python/cli/full.py

from cmd import Cmd

class MyPrompt(Cmd):
    prompt = 'pb> '
    intro = "Welcome! Type ? to list commands"

    def do_exit(self, inp):
        print("Bye")
        return True
    
    def help_exit(self):
        print('exit the application. Shorthand: x q Ctrl-D.')

    def do_add(self, inp):
        print("adding '{}'".format(inp))

    def help_add(self):
        print("Add a new entry to the system.")

    def default(self, inp):
        if inp == 'x' or inp == 'q':
            return self.do_exit(inp)

        print("Default: {}".format(inp))

    do_EOF = do_exit
    help_EOF = help_exit

if __name__ == '__main__':
    MyPrompt().cmdloop()

See also the Cmd wiki page.