summaryrefslogtreecommitdiff
path: root/docs/features/commands.rst
blob: 66745469e28f1c672c69a71650fc0c0c4150926b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
Commands
========

.. _cmd: https://docs.python.org/3/library/cmd.html

``cmd2`` is designed to make it easy for you to create new commands. These
commmands form the backbone of your application. If you started writing your
application using cmd_, all the commands you have built will work when you move
to ``cmd2``. However, there are many more capabilities available in ``cmd2``
which you can take advantage of to add more robust features to your commands,
and which makes your commands easier to write. Before we get to all the good
stuff, let's briefly discuss how to create a new command in your application.


Basic Commands
--------------

The simplest ``cmd2`` application looks like this::

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


    class App(cmd2.Cmd):
        """A simple cmd2 application."""


    if __name__ == '__main__':
        import sys
        c = App()
        sys.exit(c.cmdloop())

This application subclasses ``cmd2.Cmd`` but has no code of it's own, so all
functionality (and there's quite a bit) is inherited. Lets create a simple
command in this application called ``echo`` which outputs any arguments given
to it. Add this method to the class::

    def do_echo(self, line):
        self.poutput(line)

When you type input into the ``cmd2`` prompt, the first space delimited word is
treated as the command name. ``cmd2`` looks for a method called
``do_commandname``. If it exists, it calls the method, passing the rest of the
user input as the first argument. If it doesn't exist ``cmd2`` prints an error
message. As a result of this behavior, the only thing you have to do to create
a new command is to define a new method in the class with the appropriate name.
This is exactly how you would create a command using the cmd_ module which is
part of the python standard library.

.. note::

   See :ref:`features/generating_output:Generating Output` if you are
   unfamiliar with the ``poutput()`` method.


Statements
----------

A command is passed one argument: a string which contains all the rest of the
user input. However, in ``cmd2`` this string is actually a ``Statement``
object, which is a subclass of ``str`` to retain backwards compatibility.

``cmd2`` has a much more sophsticated parsing engine than what's included in
the cmd_ module. This parsing handles:

- quoted arguments
- output redirection and piping
- multi-line commands
- shortcut, macro, and alias expansion

In addition to parsing all of these elements from the user input, ``cmd2`` also
has code to make all of these items work; it's almost transparent to you and to
the commands you write in your own application. However, by passing your
command the ``Statement`` object instead of just a plain string, you can get
visibility into what ``cmd2`` has done with the user input before your command
got it. You can also avoid writing a bunch of parsing code, because ``cmd2``
gives you access to what it has already parsed.

A ``Statement`` object is a subclass of ``str`` that contains the following
attributes:

command
    Name of the command called. You already know this because of the method
    ``cmd2`` called, but it can sometimes be nice to have it in a string, i.e.
    if you want your error messages to contain the command name.

args
    A string containing the arguments to the command with output redirection or
    piping to shell commands removed. It turns out that the "string" value of
    the ``Statement`` object has all the output redirection and piping clauses
    removed as well. Quotes remain in the string.

command_and_args
    A string of just the command and the arguments, with output redirection or
    piping to shell commands removed.

argv
    A list of arguments a-la ``sys.argv``, including the command as ``argv[0]``
    and the subsequent arguments as additional items in the list. Quotes around
    arguments will be stripped as will any output redirection or piping
    portions of the command.

raw
    Full input exactly as typed by the user.

terminator
    Character used to end a multiline command. You can configure multiple
    termination characters, and this attribute will tell you which one the user
    typed.

For many simple commands, like the ``echo`` command above, you can ignore the
``Statement`` object and all of it's attributes and just use the passed value
as a string. You might choose to use the ``argv`` attribute to do more
sophisticated argument processing. Before you go too far down that path, you
should check out the :ref:`features/argument_processing:Argument Processing`
functionality included with ``cmd2``.


Return Values
-------------

Most commands should return nothing (either by omitting a ``return`` statement,
or by ``return None``. This indicates that your command is finished (with or
without errors), and that ``cmd2`` should prompt the user for more input.

If you return ``True`` from a command method, that indicates to ``cmd2`` that
it should stop prompting for user input and cleanly exit. ``cmd2`` already
includes a ``quit`` command, but if you wanted to make another one called
``finish`` you could::

    def do_finish(self, line):
        """Exit the application"""
        return True


Exit Codes
----------

``cmd2`` has basic infrastructure to support sh/ksh/csh/bash type exit codes.
The ``cmd2.Cmd`` object sets an ``exit_code`` attribute to zero when it is
instantiated. The value of this attribute is returned from the ``cmdloop()``
call. Therefore, if you don't do anything with this attribute in your code,
``cmdloop()`` will (almost) always return zero. There are a few built-in
``cmd2`` commands which set ``exit_code`` to ``1`` if an error occurs.

You can use this capability to easily return your own values to the operating
system shell::

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


    class App(cmd2.Cmd):
        """A simple cmd2 application."""

    def do_bail(self, line):
        """Exit the application"""
        self.perror("fatal error, exiting")
        self.exit_code = 2
        return true

    if __name__ == '__main__':
        import sys
        c = App()
        sys.exit(c.cmdloop())

If the app was run from the `bash` operating system shell, then you would see
the following interaction::

    (Cmd) bail
    fatal error, exiting
    $ echo $?
    2


Raising ``SystemExit(code)`` or calling ``sys.exit(code)`` in a command
or hook function also sets ``self.exit_code`` and stops the program.

Exception Handling
------------------

You may choose to catch and handle any exceptions which occur in
a command method. If the command method raises an exception, ``cmd2`` will
catch it and display it for you. The `debug` :ref:`setting
<features/settings:Settings>` controls how the exception is displayed. If
`debug` is `false`, which is the default, ``cmd2`` will display the exception
name and message. If `debug` is `true`, ``cmd2`` will display a traceback, and
then display the exception name and message.

There are a few exceptions which commands can raise that do not print as
described above:

- :attr:`cmd2.exceptions.SkipPostcommandHooks` - all postcommand hooks are
  skipped and no exception prints
- :attr:`cmd2.exceptions.Cmd2ArgparseError` - behaves like
  ``SkipPostcommandHooks``
- ``SystemExit`` - ``stop`` will be set to ``True`` in an attempt to stop the
  command loop
- ``KeyboardInterrupt`` - raised if running in a text script and ``stop`` isn't
  already True to stop the script

All other ``BaseExceptions`` are not caught by ``cmd2`` and will be raised

Disabling or Hiding Commands
----------------------------

See :ref:`features/disable_commands:Disabling Commands` for details of how
to:

- remove commands included in ``cmd2``
- hide commands from the help menu
- disable and re-enable commands at runtime


Modular Commands and Loading/Unloading Commands
-----------------------------------------------

See :ref:`features/modular_commands:Modular Commands` for details of how
to:

- Define commands in separate CommandSet modules
- Load or unload commands at runtime