Skip to content

# Getting Started

Here's a quick walkthrough of the simple getting_started.py example application which demonstrates many features of `cmd2`:

- Settings
- Commands
- Argument Processing
- Generating Output
- Help
- Shortcuts
- Multiline Commands
- History
- Bottom Toolbar

If you don't want to type as we go, here is the complete source (you can click to expand and then click the Copy button in the top-right):

getting_started.py

    #!/usr/bin/env python3
    """A simple example cmd2 application demonstrating many common features.

    Features demonstrated include all of the following:
     1) Colorizing/stylizing output
     2) Using multiline commands
     3) Persistent history
     4) How to run an initialization script at startup
     5) How to group and categorize commands when displaying them in help
     6) Opting-in to using the ipy command to run an IPython shell
     7) Allowing access to your application in py and ipy
     8) Displaying an intro banner upon starting your application
     9) Using a custom prompt
    10) How to make custom attributes settable at runtime.
    11) Shortcuts for commands
    12) Persistent bottom toolbar with realtime status updates
    """

    import pathlib
    import threading
    import time

    from prompt_toolkit.formatted_text import FormattedText
    from rich.style import Style

    import cmd2
    from cmd2 import (
        Color,
        stylize,
    )

    class BasicApp(cmd2.Cmd):
        """Cmd2 application to demonstrate many common features."""

        DEFAULT_CATEGORY = "My Custom Commands"

        def __init__(self) -> None:
            """Initialize the cmd2 application."""
            # Startup script that defines a couple aliases for running shell commands
            alias_script = pathlib.Path(__file__).absolute().parent / ".cmd2rc"

            # Create a shortcut for one of our commands
            shortcuts = cmd2.DEFAULT_SHORTCUTS
            shortcuts.update({"&": "intro"})
            super().__init__(
                auto_suggest=True,
                bottom_toolbar=True,
                include_ipy=True,
                multiline_commands=["echo"],
                persistent_history_file="cmd2_history.dat",
                shortcuts=shortcuts,
                startup_script=str(alias_script),
            )

            # Spawn a background thread to refresh the bottom toolbar twice a second.
            # This is necessary because the toolbar contains a timestamp that we want to keep current.
            self._stop_refresh = False
            self._refresh_thread = threading.Thread(target=self._refresh_bottom_toolbar, daemon=True)
            self._refresh_thread.start()

            # Prints an intro banner once upon application startup
            self.intro = (
                stylize(
                    "Welcome to cmd2!",
                    style=Style(color=Color.GREEN1, bgcolor=Color.GRAY0, bold=True),
                )
                + " Note the full Unicode support:  😇 💩"
                + " and the persistent bottom bar with realtime status updates!"
            )

            # Show this as the prompt when asking for input
            self.prompt = "myapp> "

            # Used as prompt for multiline commands after the first line
            self.continuation_prompt = "... "

            # Allow access to your application in py and ipy via self
            self.self_in_py = True

            # Color to output text in with echo command
            self.foreground_color = Color.CYAN.value

            # Make echo_fg settable at runtime
            fg_colors = [c.value for c in Color]
            self.add_settable(
                cmd2.Settable(
                    "foreground_color",
                    str,
                    "Foreground color to use with echo command",
                    self,
                    choices=fg_colors,
                )
            )

        def get_rprompt(self) -> str | FormattedText | None:
            current_working_directory = pathlib.Path.cwd()
            style = "bg:ansired fg:ansiwhite"
            text = f"cwd={current_working_directory}"
            return FormattedText([(style, text)])

        def _refresh_bottom_toolbar(self) -> None:
            """Background thread target to refresh the bottom toolbar.

            This is a toy example to show how the bottom toolbar can be used to display
            realtime status updates in an otherwise line-oriented command interpreter.
            """
            import contextlib

            from prompt_toolkit.application.current import get_app

            while not self._stop_refresh:
                with contextlib.suppress(Exception):
                    # get_app() will return the currently running prompt-toolkit application
                    app = get_app()
                    if app:
                        app.invalidate()
                time.sleep(0.5)

        def do_intro(self, _: cmd2.Statement) -> None:
            """Display the intro banner."""
            self.poutput(self.intro)

        def do_echo(self, arg: cmd2.Statement) -> None:
            """Multiline command."""
            self.poutput(
                stylize(
                    arg,
                    style=Style(color=self.foreground_color),
                )
            )

    if __name__ == "__main__":
        import sys

        app = BasicApp()
        sys.exit(app.cmdloop())

## Basic Application

First we need to create a new `cmd2` application. Create a new file `getting_started.py` with the following contents:

    #!/usr/bin/env python
    """A basic cmd2 application."""
    import cmd2

    class BasicApp(cmd2.Cmd):
        """Cmd2 application to demonstrate many common features."""

    if __name__ == '__main__':
        import sys
        app = BasicApp()
        sys.exit(app.cmdloop())

We have a new class `BasicApp` which is a subclass of cmd2.Cmd. When we tell Python to run our file like this:

    $ python getting_started.py

The application creates an instance of our class, and calls the cmd2.Cmd.cmdloop method. This method accepts user input and runs commands based on that input. Because we subclassed `cmd2.Cmd`, our new app already has a bunch of built-in features.

Congratulations, you have a working `cmd2` app. You can run it, and then type `quit` to exit.

## Create a New Setting

Before we create our first command, we are going to add a new setting to this app. `cmd2` includes robust support for Settings. You configure settings during object initialization, so we need to add an initializer to our class:

    def __init__(self):
        super().__init__()

        # Make maxrepeats settable at runtime
        self.maxrepeats = 3
        self.add_settable(cmd2.Settable('maxrepeats', int, 'max repetitions for speak command', self))

In that initializer, the first thing to do is to make sure we initialize `cmd2`. That's what the `super().__init__()` line does. Next create an attribute to hold the setting. Finally, call the cmd2.Cmd.add_settable method with a new instance of a cmd2.utils.Settable class. Now if you run the script, and enter the `set` command to see the settings, like this:

    $ python getting_started.py
    (Cmd) set

you will see our `maxrepeats` setting show up with its default value of `3`.

## Create A Command

Now we will create our first command, called `speak`, which will echo back whatever we tell it to say. We are going to use an argument processor so the `speak` command can shout and talk Pig Latin. We will also use some built in methods for generating output. Add this code to `getting_started.py`, so that the `speak_parser` attribute and the `do_speak()` method are part of the `BasicApp()` class:

    speak_parser = cmd2.Cmd2ArgumentParser()
    speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay')
    speak_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE')
    speak_parser.add_argument('-r', '--repeat', type=int, help='output [n] times')
    speak_parser.add_argument('words', nargs='+', help='words to say')

    @cmd2.with_argparser(speak_parser)
    def do_speak(self, args):
        """Repeats what you tell me to."""
        words = []
        for word in args.words:
            if args.piglatin:
                word = '%s%say' % (word[1:], word[0])
            if args.shout:
                word = word.upper()
            words.append(word)
        repetitions = args.repeat or 1
        for _ in range(min(repetitions, self.maxrepeats)):
            # .poutput handles newlines, and accommodates output redirection too
            self.poutput(' '.join(words))

Up at the top of the script, you'll also need to add:

    import argparse

There's a bit to unpack here, so let's walk through it. We created `speak_parser`, which uses the argparse module from the Python standard library to parse command line input from a user. So far, there is nothing specific to `cmd2`.

There is also a new method called `do_speak()`. In both cmd and `cmd2`, methods that start with `do_` become new commands, so by defining this method we have created a command called `speak`.

Note the `cmd2.decorators.with_argparser` decorator on the `do_speak()` method. This decorator does 3 useful things for us:

1. It tells `cmd2` to process all input for the `speak` command using the argparser we defined. If the user input doesn't meet the requirements defined by the argparser, then an error will be displayed for the user.
2. It alters our `do_speak` method so that instead of receiving the raw user input as a parameter, we receive the namespace from the argument parser.
3. It creates a help message for us based on the argparser.

You can see in the body of the method how we use the namespace from the argparser (passed in as the variable `args`). We build a list of words which we will output, honoring both the `--piglatin` and `--shout` options.

At the end of the method, we use our `maxrepeats` setting as an upper limit to the number of times we will print the output.

The last thing you'll notice is that we used the `self.poutput()` method to display our output. `poutput()` is a method provided by `cmd2`, which I strongly recommend you use any time you want to generate output. It provides the following benefits:

1. Allows the user to redirect output to a text file or pipe it to a shell process
2. Gracefully handles `BrokenPipeError` exceptions for redirected output
3. Honors the setting to strip embedded ANSI sequences (typically used for background and foreground colors)

Go run the script again, and try out the `speak` command. Try typing `help speak`, and you will see a lovely usage message describing the various options for the command.

With those few lines of code, we created a command, used an Argument Processor, added a nice help message for our users, and generated some output.

## Shortcuts

`cmd2` has several capabilities to simplify repetitive user input: Shortcuts, Aliases, and Macros. Let's add a shortcut to our application. Shortcuts are character strings that can be used instead of a command name. For example, `cmd2` has support for a shortcut `!` which runs the `shell` command. So instead of typing this:

    (Cmd) shell ls -al

you can type this:

    (Cmd) !ls -al

Let's add a shortcut for our `speak` command. Change the `__init__()` method so it looks like this:

    def __init__(self):
        shortcuts = cmd2.DEFAULT_SHORTCUTS
        shortcuts.update({'&': 'speak'})
        super().__init__(shortcuts=shortcuts)

        # Make maxrepeats settable at runtime
        self.maxrepeats = 3
        self.add_settable(cmd2.Settable('maxrepeats', int, 'max repetitions for speak command', self))

Shortcuts are passed to the `cmd2` initializer, and if you want the built-in shortcuts of `cmd2` you have to pass them. These shortcuts are defined as a dictionary, with the key being the shortcut, and the value containing the command. When using the default shortcuts and adding your own, it's a good idea to use the `.update()` method to modify the dictionary. This way, if you add a shortcut that happens to already be in the default set, yours will override, and you won't get any errors at runtime.

Run your app again, and type:

    (Cmd) shortcuts

to see the list of all the shortcuts, including the one for speak that we just created.

## Multiline Commands

Some use cases benefit from commands that span more than one line. For example, you might want the ability for your user to type in a SQL command, which can often span lines and which are terminated with a semicolon. Let's add a multiline command to our application. First we'll create a new command called `orate`. This code shows both the definition of our `speak` command, and the `orate` command:

    @cmd2.with_argparser(speak_parser)
    def do_speak(self, args):
        """Repeats what you tell me to."""
        words = []
        for word in args.words:
            if args.piglatin:
                word = '%s%say' % (word[1:], word[0])
            if args.shout:
                word = word.upper()
            words.append(word)
        repetitions = args.repeat or 1
        for _ in range(min(repetitions, self.maxrepeats)):
            # .poutput handles newlines, and accommodates output redirection too
            self.poutput(' '.join(words))

    # orate is a synonym for speak which takes multiline input
    do_orate = do_speak

With the new command created, we need to tell `cmd2` to treat that command as a multi-line command. Modify the super initialization line to look like this:

    super().__init__(multiline_commands=['orate'], shortcuts=shortcuts)

Now when you run the example, you can type something like this:

    (Cmd) orate O for a Muse of fire, that would ascend
    > The brightest heaven of invention,
    > A kingdom for a stage, princes to act
    > And monarchs to behold the swelling scene! ;

Notice the prompt changes to indicate that input is still ongoing. `cmd2` will continue prompting for input until it sees an unquoted semicolon (the default multi-line command termination character).

## History

`cmd2` tracks the history of the commands that users enter. As a developer, you don't need to do anything to enable this functionality, you get it for free. If you want the history of commands to persist between invocations of your application, you'll need to do a little work. The History page has all the details.

Users can access command history using two methods:

- The prompt-toolkit library which provides a pure Python replacement for the GNU readline library which is fully cross-platform compatible
- The `history` command which is built-in to `cmd2`

From the prompt in a `cmd2`-based application, you can press `Control-p` to move to the previously entered command, and `Control-n` to move to the next command. You can also search through the command history using `Control-r`.

By default, `prompt-toolkit` provides Emacs-style key bindings which will be familiar to users of the GNU Readline library. You can refer to the readline cheat sheet or you can dig into the Prompt Toolkit User Manual for all the details, including instructions for customizing the key bindings.

The `history` command allows a user to view the command history, and select commands from history by number, range, string search, or regular expression. With the selected commands, users can:

- Re-run the commands
- Edit the selected commands in a text editor, and run them after the text editor exits
- Save the commands to a file
- Run the commands, saving both the commands and their output to a file

Learn more about the `history` command by typing `history -h` at any `cmd2` input prompt, or by exploring Command History For Users.

## Conclusion

You've just created a simple, but functional command line application. With minimal work on your part, the application leverages many robust features of `cmd2`. To learn more you can:

- Dive into all of the Features that `cmd2` provides
- Look at more Examples
- Browse the API Reference